home *** CD-ROM | disk | FTP | other *** search
/ 100 Best Shareware & Freeware Games / 100 Games.iso / Cards / PySol / pysol460.exe / {app} / data / pysol.pyc (.txt) next >
Encoding:
Python Compiled Bytecode  |  2001-07-27  |  1.1 MB  |  26,580 lines

  1. # Source Generated with Decompyle++
  2. # File: in.pyc (Python 1.5)
  3.  
  4. PACKAGE = 'PySol'
  5. PACKAGE_URL = 'http://pysol.tsx.org'
  6. import os
  7. import sys
  8.  
  9. try:
  10.     bundle
  11. except:
  12.     bundle = 1
  13.     if os.name == 'nt':
  14.         bundle = 1 | 2
  15.     
  16.  
  17. if not hasattr(sys, 'platform'):
  18.     sys.platform = 'unknown'
  19.  
  20. if sys.platform[:4] != 'java':
  21.     if sys.version[:5] < '1.5.2':
  22.         print '%s needs Python 1.5.2 or better (you have %s)' % (PACKAGE, sys.version)
  23.         sys.exit(2)
  24.     
  25.  
  26.  
  27. try:
  28.     import __builtin__
  29.     import glob
  30.     import math
  31.     import operator
  32.     import re
  33.     import string
  34.     import time
  35.     import types
  36.     import StringIO
  37.     import formatter
  38.     import htmllib
  39.     UserDict
  40.     UserList
  41.     if os.name == 'mac':
  42.         pass
  43. except ImportError:
  44.     ex = None
  45.     
  46.     try:
  47.         import traceback
  48.         traceback.print_exc()
  49.     except:
  50.         pass
  51.  
  52.     print '\n%s cannot find the standard Python libraries.\nPlease check your Python installation.\n' % PACKAGE
  53.     sys.exit(2)
  54.  
  55.  
  56. try:
  57.     UnpicklingError
  58. except ImportError:
  59.     UnpicklingError
  60. except:
  61.     Unpickler
  62.  
  63. thread = None
  64.  
  65. try:
  66.     import thread
  67. except:
  68.     Unpickler
  69.     Pickler
  70.     import pickle
  71.     thread = None
  72.  
  73. pysolsoundserver = None
  74. win32api = None
  75. if os.name == 'nt':
  76.     
  77.     try:
  78.         import win32api
  79.     except:
  80.         import pickle if thread and not ('--nosound' in sys.argv[1:]) else Pickler
  81.         win32api = None
  82.  
  83.  
  84. traceback = None
  85.  
  86. try:
  87.     import Tkinter
  88.     import Canvas
  89.     import tkColorChooser
  90.     import tkFileDialog
  91. except ImportError:
  92.     ex = None
  93.     
  94.     try:
  95.         import traceback
  96.         traceback.print_exc()
  97.     except:
  98.         pass
  99.  
  100.     print '\n%s cannot find the Tkinter Python libraries.\nPlease check your Python installation.\n' % PACKAGE
  101.     sys.exit(2)
  102.  
  103. TclError = Tkinter.TclError
  104. VERSION = '4.60'
  105. VERSION_DATE = '02 Aug 2000'
  106. VERSION_MAJOR = 4
  107. VERSION_MINOR = 60
  108. VERSION_TUPLE = (4, 60)
  109.  
  110. def indices(object):
  111.     return tuple(range(len(object)))
  112.  
  113.  
  114. def trange(start, stop = None, step = None):
  115.     if stop is None:
  116.         return tuple(range(start))
  117.     elif step is None:
  118.         return tuple(range(start, stop))
  119.     else:
  120.         return tuple(range(start, stop, step))
  121.  
  122.  
  123. def range_len(object):
  124.     return range(len(object))
  125.  
  126.  
  127. def reverse(sequence):
  128.     if type(sequence) is types.TupleType:
  129.         l = list(sequence)
  130.         l.reverse()
  131.         l = tuple(l)
  132.     elif type(sequence) is types.ListType:
  133.         l = sequence[:]
  134.         l.reverse()
  135.     else:
  136.         l = list(sequence)
  137.         l.reverse()
  138.     return l
  139.  
  140.  
  141. def irange(object, indices = None):
  142.     pass
  143.  
  144.  
  145. def count(condition, sequence):
  146.     if condition is None:
  147.         return len(filter(None, sequence))
  148.     else:
  149.         return len(filter(None, map(condition, sequence)))
  150.  
  151.  
  152. def exists(condition, sequence):
  153.     if condition is None:
  154.         condition = operator.truth
  155.     
  156.     for obj in sequence:
  157.         pass
  158.     
  159.     return 0
  160.  
  161.  
  162. def forall(condition, sequence):
  163.     if condition is None:
  164.         condition = operator.truth
  165.     
  166.     for obj in sequence:
  167.         pass
  168.     
  169.     return 1
  170.  
  171. bool = operator.truth
  172.  
  173. def sgn(expr):
  174.     if expr < 0:
  175.         return -1
  176.     
  177.     if expr > 0:
  178.         return 1
  179.     
  180.     return 0
  181.  
  182. EnvError = (IOError, OSError, os.error)
  183.  
  184. class SubclassResponsibility(Exception):
  185.     pass
  186.  
  187.  
  188. def static(f, *args, **kw):
  189.     if args:
  190.         a = tuple([
  191.             f.im_class()] + list(args))
  192.     else:
  193.         a = (f.im_class(),)
  194.     return apply(f, a, kw)
  195.  
  196.  
  197. def ifelse(expr, val1, val2):
  198.     if expr:
  199.         return val1
  200.     
  201.     return val2
  202.  
  203.  
  204. def merge_dict(dict1, dict2, merge_none = 1):
  205.     for k, v in dict2.items():
  206.         if dict1.has_key(k):
  207.             if type(dict1[k]) is type(v):
  208.                 dict1[k] = v
  209.             elif dict1[k] is None and merge_none:
  210.                 dict1[k] = v
  211.             
  212.         
  213.     
  214.  
  215.  
  216. def latin1_to_ascii(n):
  217.     n = re.sub('[\\xc4]', 'Ae', n)
  218.     n = re.sub('[\\xd6]', 'Oe', n)
  219.     n = re.sub('[\\xdc]', 'Ue', n)
  220.     n = re.sub('[\\xe4]', 'ae', n)
  221.     n = re.sub('[\\xf6]', 'oe', n)
  222.     n = re.sub('[\\xfc]', 'ue', n)
  223.     return n
  224.  
  225. htmlentitydefs_i = { }
  226.  
  227. def latin1_to_html(n):
  228.     (s, g) = ('', htmlentitydefs_i.get)
  229.     for c in n:
  230.         s = s + g(c, c)
  231.     
  232.     return s
  233.  
  234.  
  235. def hexify(s):
  236.     return '%02x' * len(s) % tuple(map(ord, s))
  237.  
  238.  
  239. def getusername():
  240.     user = None
  241.     if os.name == 'nt':
  242.         
  243.         try:
  244.             user = string.strip(win32api.GetUserName())
  245.         except:
  246.             pass
  247.  
  248.     
  249.     if not user:
  250.         user = string.strip(os.environ.get('USER', ''))
  251.     
  252.     if not user:
  253.         user = string.strip(os.environ.get('LOGNAME', ''))
  254.     
  255.     return user
  256.  
  257.  
  258. def gethomedir():
  259.     default_home = os.curdir
  260.     if os.name == 'nt':
  261.         default_home = 'c:\\'
  262.     
  263.     home = string.strip(os.environ.get('HOME', ''))
  264.     if not home or not os.path.isdir(home):
  265.         if os.name == 'nt':
  266.             home = os.environ.get('HOMEDRIVE', '') + os.environ.get('HOMEPATH', '')
  267.         
  268.     
  269.     if not home or not os.path.isdir(home):
  270.         home = default_home
  271.     
  272.     return os.path.abspath(home)
  273.  
  274.  
  275. def getprefdir(package, home = None):
  276.     if os.name == 'mac':
  277.         (vrefnum, dirid) = macfs.FindFolder(MACFS.kOnSystemDisk, MACFS.kPreferencesFolderType, 0)
  278.         fss = macfs.FSSpec((vrefnum, dirid, ':' + package))
  279.         return fss.as_pathname()
  280.     
  281.     if os.name == 'nt' and win32api:
  282.         
  283.         try:
  284.             windir = os.path.abspath(win32api.GetWindowsDirectory())
  285.             if windir and os.path.isdir(windir):
  286.                 user = string.strip(win32api.GetUserName())
  287.                 return os.path.join(windir, 'Preferences', package, user)
  288.         except:
  289.             pass
  290.  
  291.     
  292.     if home is None:
  293.         home = gethomedir()
  294.     
  295.     return os.path.join(home, '.' + string.lower(package))
  296.  
  297. uclock = time.clock
  298. usleep = time.sleep
  299. if os.name == 'posix':
  300.     uclock = time.time
  301.  
  302.  
  303. def destruct(obj):
  304.     pass
  305.  
  306.  
  307. class Struct:
  308.     
  309.     def __init__(_, **kw):
  310.         _.__dict__.update(kw)
  311.  
  312.     
  313.     def __str__(_):
  314.         return str(_.__dict__)
  315.  
  316.     
  317.     def addattr(_, **kw):
  318.         for key in kw.keys():
  319.             pass
  320.         
  321.         _.__dict__.update(kw)
  322.  
  323.     
  324.     def update(_, dict):
  325.         for key in dict.keys():
  326.             pass
  327.         
  328.         _.__dict__.update(dict)
  329.  
  330.     
  331.     def clear(_):
  332.         for key in _.__dict__.keys():
  333.             t = type(key)
  334.             if t is types.ListType:
  335.                 _.__dict__[key] = []
  336.             elif t is types.TupleType:
  337.                 _.__dict__[key] = ()
  338.             elif t is types.DictType:
  339.                 _.__dict__[key] = { }
  340.             else:
  341.                 _.__dict__[key] = None
  342.         
  343.  
  344.     
  345.     def copy(_):
  346.         c = Struct()
  347.         c.__class__ = _.__class__
  348.         c.__dict__.update(_.__dict__)
  349.         return c
  350.  
  351.  
  352.  
  353. def kwdefault(kw, **defaults):
  354.     for k, v in defaults.items():
  355.         pass
  356.     
  357.  
  358.  
  359. class KwStruct:
  360.     
  361.     def __init__(_, kw = { }, **defaults):
  362.         if isinstance(kw, KwStruct):
  363.             kw = kw.__dict__
  364.         
  365.         if isinstance(defaults, KwStruct):
  366.             defaults = defaults.__dict__
  367.         
  368.         if defaults:
  369.             kw = kw.copy()
  370.             for k, v in defaults.items():
  371.                 pass
  372.             
  373.         
  374.         _.__dict__.update(kw)
  375.  
  376.     
  377.     def __setattr__(_, key, value):
  378.         if not _.__dict__.has_key(key):
  379.             raise AttributeError, key
  380.         
  381.         _.__dict__[key] = value
  382.  
  383.     
  384.     def __getitem__(_, key):
  385.         return getattr(_, key)
  386.  
  387.     
  388.     def get(_, key, default = None):
  389.         return _.__dict__.get(key, default)
  390.  
  391.     
  392.     def getKw(_):
  393.         return _.__dict__
  394.  
  395.  
  396.  
  397. def pickle(obj, filename, binmode = 0):
  398.     f = None
  399.     
  400.     try:
  401.         f = open(filename, 'wb')
  402.         p = Pickler(f, binmode)
  403.         p.dump(obj)
  404.         f.close()
  405.         f = None
  406.     finally:
  407.         if f:
  408.             f.close()
  409.         
  410.  
  411.  
  412.  
  413. def unpickle(filename):
  414.     (f, obj) = (None, None)
  415.     
  416.     try:
  417.         f = open(filename, 'rb')
  418.         p = Unpickler(f)
  419.         x = p.load()
  420.         f.close()
  421.         f = None
  422.         obj = x
  423.     finally:
  424.         if f:
  425.             f.close()
  426.         
  427.  
  428.     return obj
  429.  
  430.  
  431. def spawnv(file, args = ()):
  432.     if not args:
  433.         args = ()
  434.     
  435.     args = (file,) + tuple(args)
  436.     if not os.path.isfile(file):
  437.         raise os.error, str(file)
  438.     
  439.     mode = os.stat(file)[0]
  440.     if not (mode & 64):
  441.         return 0
  442.     
  443.     if os.name == 'nt':
  444.         os.spawnv(os.P_DETACH, file, args)
  445.         return 1
  446.     elif os.name == 'posix':
  447.         pid = os.fork()
  448.         if pid == -1:
  449.             raise os.error, 'fork failed'
  450.         
  451.         if pid != 0:
  452.             
  453.             try:
  454.                 os.waitpid(pid, 0)
  455.             except:
  456.                 pass
  457.  
  458.             return 1
  459.         
  460.         for fd in range(255, -1, -1):
  461.             
  462.             try:
  463.                 os.close(fd)
  464.             except:
  465.                 0
  466.                 range(255, -1, -1)
  467.  
  468.         
  469.         
  470.         try:
  471.             fd = os.open('/dev/null', os.O_RDWR)
  472.             os.dup(fd)
  473.             os.dup(fd)
  474.         except:
  475.             0
  476.             range(255, -1, -1)
  477.  
  478.         
  479.         try:
  480.             if os.fork() == 0:
  481.                 
  482.                 try:
  483.                     os.setpgrp()
  484.                 except:
  485.                     0
  486.                     range(255, -1, -1)
  487.  
  488.                 os.execv(file, args)
  489.         except:
  490.             0
  491.             range(255, -1, -1)
  492.  
  493.         while 1:
  494.             os._exit(0)
  495.             continue
  496.             0
  497.     
  498.     return 0
  499.  
  500.  
  501. def spawnvp(file, args = ()):
  502.     if file and os.path.isabs(file):
  503.         
  504.         try:
  505.             if spawnv(file, args):
  506.                 return file
  507.         except:
  508.             pass
  509.  
  510.         return None
  511.     
  512.     path = os.environ.get('PATH', '')
  513.     path = string.splitfields(path, os.pathsep)
  514.     for dir in path:
  515.         
  516.         try:
  517.             if dir and os.path.isdir(dir):
  518.                 f = os.path.join(dir, file)
  519.                 
  520.                 try:
  521.                     if spawnv(f, args):
  522.                         return f
  523.                 except:
  524.                     0
  525.                     path
  526.  
  527.         except:
  528.             0
  529.             path
  530.  
  531.     
  532.     return None
  533.  
  534.  
  535. def openURL(url):
  536.     if os.name == 'nt':
  537.         SW_HIDE = 0
  538.         SW_SHOWNORMAL = 1
  539.         SW_SHOW = 5
  540.         
  541.         try:
  542.             handle = win32api.ShellExecute(0, 'open', url, None, '.', SW_SHOW)
  543.             return 1
  544.         except:
  545.             pass
  546.  
  547.     
  548.     return 0
  549.  
  550.  
  551. class PysolRandom:
  552.     MAX_SEED = 0x0L
  553.     ORIGIN_UNKNOWN = 0
  554.     ORIGIN_RANDOM = 1
  555.     ORIGIN_PREVIEW = 2
  556.     ORIGIN_SELECTED = 3
  557.     ORIGIN_NEXT_GAME = 4
  558.     
  559.     def __init__(_, seed = None):
  560.         if seed is None:
  561.             seed = _._getRandomSeed()
  562.         
  563.         _.initial_seed = _.setSeed(seed)
  564.         _.origin = _.ORIGIN_UNKNOWN
  565.  
  566.     
  567.     def __str__(_):
  568.         return _.str(_.initial_seed)
  569.  
  570.     
  571.     def reset(_):
  572.         _.seed = _.initial_seed
  573.  
  574.     
  575.     def getSeed(_):
  576.         return _.seed
  577.  
  578.     
  579.     def setSeed(_, seed):
  580.         seed = _._convertSeed(seed)
  581.         if type(seed) is not types.LongType:
  582.             raise TypeError, 'seeds must be longs'
  583.         
  584.         if not None if seed <= seed else seed <= _.MAX_SEED:
  585.             raise ValueError, 'seed out of range'
  586.         
  587.         _.seed = seed
  588.         return seed
  589.  
  590.     
  591.     def copy(_):
  592.         random = PysolRandom(0x0L)
  593.         random.__class__ = _.__class__
  594.         random.__dict__.update(_.__dict__)
  595.         return random
  596.  
  597.     
  598.     def choice(_, seq):
  599.         return seq[int(_.random() * len(seq))]
  600.  
  601.     
  602.     def randint(_, a, b):
  603.         return a + int(_.random() * (b + 1 - a))
  604.  
  605.     
  606.     def random(_):
  607.         raise SubclassResponsibility
  608.  
  609.     
  610.     def _convertSeed(_, seed):
  611.         return long(seed)
  612.  
  613.     
  614.     def increaseSeed(_, seed):
  615.         if seed < _.MAX_SEED:
  616.             return seed + 0x1L
  617.         
  618.         return 0x0L
  619.  
  620.     
  621.     def _getRandomSeed(_):
  622.         t = long(time.time() * 256.0)
  623.         t = (t ^ t >> 24) % (_.MAX_SEED + 0x1L)
  624.         return t
  625.  
  626.     
  627.     def shuffle(_, seq):
  628.         n = len(seq) - 1
  629.         while n > 0:
  630.             j = _.randint(0, n)
  631.             (seq[n], seq[j]) = (seq[j], seq[n])
  632.             n = n - 1
  633.  
  634.  
  635.  
  636. class LCRandom64(PysolRandom):
  637.     MAX_SEED = 0xFFFFFFFFFFFFFFFFL
  638.     
  639.     def str(_, seed):
  640.         s = repr(long(seed))[:-1]
  641.         s = '0' * (20 - len(s)) + s
  642.         return s
  643.  
  644.     
  645.     def random(_):
  646.         _.seed = _.seed * 0x5851F42D4C957F2DL + 0x1L & _.MAX_SEED
  647.         return (_.seed >> 21 & 0x7FFFFFFFL) / 2147483648.0
  648.  
  649.  
  650.  
  651. class LCRandom31(PysolRandom):
  652.     MAX_SEED = 0x7FFFFFFFL
  653.     
  654.     def str(_, seed):
  655.         return '%05d' % int(seed)
  656.  
  657.     
  658.     def random(_):
  659.         _.seed = _.seed * 0x343FDL + 0x269EC3L & _.MAX_SEED
  660.         return (_.seed >> 16) / 32768.0
  661.  
  662.     
  663.     def randint(_, a, b):
  664.         _.seed = _.seed * 0x343FDL + 0x269EC3L & _.MAX_SEED
  665.         return a + int(_.seed >> 16) % (b + 1 - a)
  666.  
  667.  
  668.  
  669. class WHRandom(PysolRandom):
  670.     MAX_SEED = 0x763CL * 0x7662L * 0x7672L - 1
  671.     
  672.     def str(_, seed):
  673.         (x, y, z) = _._unpackSeed(seed)
  674.         return '%04x-%04x-%04x' % (x, y, z)
  675.  
  676.     
  677.     def random(_):
  678.         (x, y, z) = _._unpackSeed(_.seed)
  679.         x = 171 * x % 30269
  680.         y = 172 * y % 30307
  681.         z = 170 * z % 30323
  682.         _.seed = _._packSeed(x, y, z)
  683.         return (x / 30269.0 + y / 30307.0 + z / 30323.0) % 1.0
  684.  
  685.     
  686.     def _convertSeed(_, seed):
  687.         if type(seed) is types.TupleType:
  688.             return _._packSeed(seed[0], seed[1], seed[2])
  689.         
  690.         return long(seed)
  691.  
  692.     
  693.     def _packSeed(_, x, y, z):
  694.         if __debug__:
  695.             if x < x:
  696.                 pass
  697.             elif not x < 30269:
  698.                 raise AssertionError
  699.         if __debug__:
  700.             if y < y:
  701.                 pass
  702.             elif not y < 30307:
  703.                 raise AssertionError
  704.         if __debug__:
  705.             if z < z:
  706.                 pass
  707.             elif not z < 30323:
  708.                 raise AssertionError
  709.         seed = ((x - 1) * 0x7662L + (y - 1)) * 0x7672L + (z - 1)
  710.         if __debug__:
  711.             if seed <= seed:
  712.                 pass
  713.             elif not seed <= _.MAX_SEED:
  714.                 raise AssertionError
  715.         return seed
  716.  
  717.     
  718.     def _unpackSeed(_, seed):
  719.         if __debug__:
  720.             if seed <= seed:
  721.                 pass
  722.             elif not seed <= _.MAX_SEED:
  723.                 raise AssertionError
  724.         (seed, z) = divmod(seed, 0x7672L)
  725.         (seed, y) = divmod(seed, 0x7662L)
  726.         (x, y, z) = (int(seed + 1), int(y + 1), int(z + 1))
  727.         if __debug__:
  728.             if x < x:
  729.                 pass
  730.             elif not x < 30269:
  731.                 raise AssertionError
  732.         if __debug__:
  733.             if y < y:
  734.                 pass
  735.             elif not y < 30307:
  736.                 raise AssertionError
  737.         if __debug__:
  738.             if z < z:
  739.                 pass
  740.             elif not z < 30323:
  741.                 raise AssertionError
  742.         return (x, y, z)
  743.  
  744.  
  745.  
  746. def constructRandom(s):
  747.     s = re.sub('L$', '', str(s))
  748.     s = re.sub('[\\s\\#\\-\\_\\.\\,]', '', string.lower(s))
  749.     if not s:
  750.         return (None, None)
  751.     
  752.     if 1 and len(s) in (12, 16):
  753.         if re.search('[^0-9a-f]', s):
  754.             raise ValueError, s
  755.         
  756.         ss = s
  757.         gameid = None
  758.         if len(s) == 16:
  759.             gameid = string.atoi(s[:4], 16)
  760.             ss = s[4:]
  761.         
  762.         x = string.atoi(ss[0:4], 16)
  763.         y = string.atoi(ss[4:8], 16)
  764.         z = string.atoi(ss[8:12], 16)
  765.         if x < x:
  766.             pass
  767.         elif x < 30269:
  768.             if y < y:
  769.                 pass
  770.             elif y < 30307:
  771.                 if z < z:
  772.                     pass
  773.                 elif z < 30323:
  774.                     return (gameid, WHRandom((x, y, z)))
  775.                 
  776.             
  777.     gameid = None
  778.     if re.search('[^0-9]', s):
  779.         raise ValueError, s
  780.     
  781.     seed = string.atol(s)
  782.     if seed <= seed:
  783.         pass
  784.     elif seed <= 32000:
  785.         return (gameid, LCRandom31(seed))
  786.     
  787.     return (gameid, LCRandom64(seed))
  788.  
  789. SUITS = ('Club', 'Spade', 'Heart', 'Diamond')
  790. COLORS = ('black', 'red')
  791. RANKS = ('Ace', '2', '3', '4', '5', '6', '7', '8', '9', '10', 'Jack', 'Queen', 'King')
  792. ACE = 0
  793. JACK = 10
  794. QUEEN = 11
  795. KING = 12
  796. ANY_SUIT = -1
  797. ANY_COLOR = -1
  798. ANY_RANK = -1
  799. NO_SUIT = 999999
  800. NO_COLOR = 999999
  801. NO_RANK = 999999
  802. NO_REDEAL = 0
  803. UNLIMITED_REDEALS = -1
  804. VARIABLE_REDEALS = -2
  805. CARDSET = 'cardset'
  806. IMAGE_EXTENSIONS = ('.gif', '.ppm')
  807. if 1 and os.name == 'nt':
  808.     IMAGE_EXTENSIONS = ('.png', '.gif', '.ppm', '.jpg')
  809.  
  810.  
  811. try:
  812.     bundle
  813. except:
  814.     bundle = 0
  815.  
  816.  
  817. def get_version_tuple(version_string):
  818.     v = re.split('[^\\d\\.]', version_string)
  819.     if not v or not v[0]:
  820.         return (0,)
  821.     
  822.     v = string.split(v[0], '.')
  823.     v = filter((lambda x: x != ''), v)
  824.     if not v or not v[0]:
  825.         return (0,)
  826.     
  827.     return tuple(map(int, v))
  828.  
  829.  
  830. class Timer:
  831.     
  832.     def __init__(_, msg = ''):
  833.         _.msg = msg
  834.         _.clock = time.time
  835.         if os.name == 'nt':
  836.             _.clock = time.clock
  837.         
  838.         _.start = _.clock()
  839.  
  840.     
  841.     def reset(_):
  842.         _.start = _.clock()
  843.  
  844.     
  845.     def get(_):
  846.         return _.clock() - _.start
  847.  
  848.     
  849.     def __repr__(_):
  850.         return '%-20s %6.3f seconds' % (_.msg, _.clock() - _.start)
  851.  
  852.  
  853.  
  854. class DataLoader:
  855.     
  856.     def __init__(_, argv0, filenames, path = []):
  857.         _.dir = None
  858.         if type(filenames) is types.StringType:
  859.             filenames = (filenames,)
  860.         
  861.         if not __debug__ and type(filenames) in (types.TupleType, types.ListType):
  862.             raise AssertionError
  863.         path = path[:]
  864.         (head, tail) = os.path.split(argv0)
  865.         if not head:
  866.             head = os.curdir
  867.         
  868.         path.append(head)
  869.         path.append(os.path.join(head, 'data'))
  870.         if os.name == 'posix':
  871.             pass
  872.         
  873.         if os.name == 'nt':
  874.             pass
  875.         
  876.         if os.name == 'mac':
  877.             pass
  878.         
  879.         _.path = []
  880.         for p in path:
  881.             
  882.             try:
  883.                 np = os.path.normpath(p)
  884.                 if np and not (np in _.path) and os.path.isdir(np):
  885.                     _.path.append(np)
  886.             except EnvError:
  887.                 None if 1 and os.name == 'posix' else (VERSION, '') if not p else path
  888.                 None if 1 and os.name == 'posix' else (VERSION, '') if not p else path
  889.             except:
  890.                 None if 1 and os.name == 'posix' else (VERSION, '') if not p else path
  891.  
  892.         
  893.         for p in _.path:
  894.             n = 0
  895.             for filename in filenames:
  896.                 
  897.                 try:
  898.                     f = os.path.join(p, filename)
  899.                     if os.path.isfile(f):
  900.                         n = n + 1
  901.                 except EnvError:
  902.                     0
  903.                     0
  904.                     filenames
  905.                 except:
  906.                     0
  907.  
  908.             
  909.         else:
  910.             raise os.error, str(argv0) + ': DataLoader could not find ' + str(filenames)
  911.  
  912.     
  913.     def __findFile(_, func, filename, subdirs = None, do_raise = 1):
  914.         if subdirs is None:
  915.             subdirs = ('',)
  916.         elif type(subdirs) is types.StringType:
  917.             subdirs = (subdirs,)
  918.         
  919.         for dir in subdirs:
  920.             f = os.path.join(_.dir, dir, filename)
  921.             f = os.path.normpath(f)
  922.         
  923.         if do_raise:
  924.             raise os.error, 'DataLoader could not find ' + filename + ' in ' + _.dir + ' ' + str(subdirs)
  925.         
  926.         return None
  927.  
  928.     
  929.     def findFile(_, filename, subdirs = None):
  930.         return _._DataLoader__findFile(os.path.isfile, filename, subdirs)
  931.  
  932.     
  933.     def findImage(_, filename, subdirs = None):
  934.         for ext in IMAGE_EXTENSIONS:
  935.             f = _._DataLoader__findFile(os.path.isfile, filename + ext, subdirs, 0)
  936.         
  937.         raise os.error, 'DataLoader could not find image ' + filename + ' in ' + _.dir + ' ' + str(subdirs)
  938.  
  939.     
  940.     def findIcon(_, filename = None, subdirs = None):
  941.         if not filename:
  942.             filename = string.lower(PACKAGE)
  943.         
  944.         (root, ext) = os.path.splitext(filename)
  945.         if not ext:
  946.             filename = filename + '.xbm'
  947.         
  948.         return _.findFile(filename, subdirs)
  949.  
  950.     
  951.     def findDir(_, filename, subdirs = None):
  952.         return _._DataLoader__findFile(os.path.isdir, filename, subdirs)
  953.  
  954.  
  955. cyclops = None
  956.  
  957. class Resource(Struct):
  958.     
  959.     def __init__(_, **kw):
  960.         kw = KwStruct(kw, name = '', filename = '', basename = '', absname = '', index = -1, error = 0)
  961.         apply(Struct.__init__, (_,), kw.getKw())
  962.  
  963.     
  964.     def getSortKey(_):
  965.         return string.lower(latin1_to_ascii(_.name))
  966.  
  967.  
  968.  
  969. class ResourceManager:
  970.     
  971.     def __init__(_):
  972.         _._selected_key = -1
  973.         _._objects = []
  974.         _._objects_by_name = None
  975.         _._objects_cache_name = { }
  976.         _._objects_cache_filename = { }
  977.         _._objects_cache_basename = { }
  978.         _._objects_cache_absname = { }
  979.  
  980.     
  981.     def getSelected(_):
  982.         return _._selected_key
  983.  
  984.     
  985.     def setSelected(_, index):
  986.         if __debug__:
  987.             if index <= index:
  988.                 pass
  989.             elif not index < len(_._objects):
  990.                 raise AssertionError
  991.         _._selected_key = index
  992.  
  993.     
  994.     def len(_):
  995.         return len(_._objects)
  996.  
  997.     
  998.     def register(_, obj):
  999.         if not __debug__ and obj.index == -1:
  1000.             raise AssertionError
  1001.         if __debug__:
  1002.             if not obj.name and not _._objects_cache_name.has_key(obj.name):
  1003.                 raise AssertionError
  1004.         _._objects_cache_name[obj.name] = obj
  1005.         if obj.filename:
  1006.             obj.absname = os.path.abspath(obj.filename)
  1007.             obj.basename = os.path.basename(obj.filename)
  1008.             _._objects_cache_filename[obj.filename] = obj
  1009.             _._objects_cache_basename[obj.basename] = obj
  1010.             _._objects_cache_absname[obj.absname] = obj
  1011.         
  1012.         obj.index = len(_._objects)
  1013.         _._objects.append(obj)
  1014.         _._objects_by_name = None
  1015.  
  1016.     
  1017.     def get(_, index):
  1018.         if index <= index:
  1019.             pass
  1020.         elif index < len(_._objects):
  1021.             return _._objects[index]
  1022.         
  1023.         return None
  1024.  
  1025.     
  1026.     def getByName(_, key):
  1027.         return _._objects_cache_name.get(key)
  1028.  
  1029.     
  1030.     def getByBasename(_, key):
  1031.         return _._objects_cache_basename.get(key)
  1032.  
  1033.     
  1034.     def getAll(_):
  1035.         return tuple(_._objects)
  1036.  
  1037.     
  1038.     def getAllSortedByName(_):
  1039.         if _._objects_by_name is None:
  1040.             l = map((lambda obj: (obj.getSortKey(), obj)), _._objects)
  1041.             l.sort()
  1042.             _._objects_by_name = tuple(map((lambda item: item[1]), l))
  1043.         
  1044.         return _._objects_by_name
  1045.  
  1046.     
  1047.     def _addDir(_, result, dir):
  1048.         
  1049.         try:
  1050.             if dir:
  1051.                 dir = os.path.normpath(dir)
  1052.                 if os.name == 'nt':
  1053.                     dir = os.path.normcase(dir)
  1054.                 
  1055.                 if dir and os.path.isdir(dir) and not (dir in result):
  1056.                     result.append(dir)
  1057.                 
  1058.         except EnvError:
  1059.             ex = None
  1060.  
  1061.  
  1062.     
  1063.     def _addRegistryKey(_, result, hkey, subkey):
  1064.         KEY_READ = 131097
  1065.         k = None
  1066.         
  1067.         try:
  1068.             k = win32api.RegOpenKeyEx(hkey, subkey, 0, KEY_READ)
  1069.             (nsubkeys, nvalues, t) = win32api.RegQueryInfoKey(k)
  1070.             for i in range(nvalues):
  1071.                 
  1072.                 try:
  1073.                     (key, value, vtype) = win32api.RegEnumValue(k, i)
  1074.                 except:
  1075.                     0
  1076.                     range(nvalues)
  1077.                     break
  1078.  
  1079.         finally:
  1080.             if k is not None:
  1081.                 
  1082.                 try:
  1083.                     win32api.RegCloseKey(k)
  1084.                 except:
  1085.                     pass
  1086.  
  1087.             
  1088.  
  1089.  
  1090.     
  1091.     def getSearchDirs(_, app, search, env = None):
  1092.         if type(search) is types.StringType:
  1093.             search = (search,)
  1094.         
  1095.         result = []
  1096.         for dir in (app.dataloader.dir, app.dn.maint, app.dn.config):
  1097.             dir = os.path.normpath(dir)
  1098.             if not dir or not os.path.isdir(dir):
  1099.                 continue
  1100.             
  1101.             for s in search:
  1102.                 
  1103.                 try:
  1104.                     if s[-2:] == '-*':
  1105.                         d = os.path.normpath(os.path.join(dir, s[:-2]))
  1106.                         _._addDir(result, d)
  1107.                         globdirs = glob.glob(d + '-*')
  1108.                         globdirs.sort()
  1109.                         for d in globdirs:
  1110.                             _._addDir(result, d)
  1111.                         
  1112.                     else:
  1113.                         _._addDir(result, os.path.join(dir, s))
  1114.                 except EnvError:
  1115.                     0
  1116.                     ex = 0
  1117.                     search
  1118.                 except:
  1119.                     0
  1120.  
  1121.             
  1122.         
  1123.         if app.debug >= 2:
  1124.             print 'getSearchDirs', env, search, '->', result
  1125.         
  1126.         return result
  1127.  
  1128.     
  1129.     def getRegistryDirs(_, app, categories):
  1130.         if not win32api:
  1131.             return []
  1132.         
  1133.         HKEY_CURRENT_USER = -2147483647
  1134.         HKEY_LOCAL_MACHINE = -2147483646
  1135.         vendors = ('Markus Oberhumer', '')
  1136.         versions = (VERSION, '')
  1137.         if type(categories) is types.StringType:
  1138.             categories = (categories,)
  1139.         
  1140.         result = []
  1141.         for version in versions:
  1142.             for vendor in vendors:
  1143.                 for category in categories:
  1144.                     t = ('Software', vendor, PACKAGE, version, category)
  1145.                     t = filter(None, t)
  1146.                     subkey = string.join(t, '\\')
  1147.                     for hkey in (HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE):
  1148.                         
  1149.                         try:
  1150.                             _._addRegistryKey(result, hkey, subkey)
  1151.                         except:
  1152.                             0
  1153.                             (HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE)
  1154.                             0
  1155.  
  1156.                     
  1157.                 
  1158.             
  1159.         
  1160.         return result
  1161.  
  1162.  
  1163.  
  1164. class CSI:
  1165.     SIZE_TINY = 1
  1166.     SIZE_SMALL = 2
  1167.     SIZE_MEDIUM = 3
  1168.     SIZE_LARGE = 4
  1169.     SIZE_XLARGE = 5
  1170.     TYPE_FRENCH = 1
  1171.     TYPE_HANAFUDA = 2
  1172.     TYPE_TAROCK = 3
  1173.     TYPE_MAHJONGG = 4
  1174.     TYPE_HEXADECK = 5
  1175.     TYPE_MUGHAL_GANJIFA = 6
  1176.     TYPE_NAVAGRAHA_GANJIFA = 7
  1177.     TYPE_DASHAVATARA_GANJIFA = 8
  1178.     TYPE_TRUMP_ONLY = 9
  1179.     TYPE = {
  1180.         1: 'French type (52 cards)',
  1181.         2: 'Hanafuda type (48 cards)',
  1182.         3: 'Tarock type (78 cards)',
  1183.         5: 'Hex A Deck type (68 cards)',
  1184.         6: 'Mughal Ganjifa type (96 cards)',
  1185.         7: 'Navagraha Ganjifa type (108 cards)',
  1186.         8: 'Dashavatara Ganjifa type (120 cards)',
  1187.         9: 'Trumps only type (variable cards)' }
  1188.     STYLE = {
  1189.         1: 'Adult',
  1190.         2: 'Animals',
  1191.         3: 'Anime',
  1192.         4: 'Art',
  1193.         5: 'Cartoons',
  1194.         6: 'Children',
  1195.         7: 'Classic look',
  1196.         8: 'Collectors',
  1197.         9: 'Computers',
  1198.         10: 'Engines',
  1199.         11: 'Fantasy',
  1200.         30: 'Ganjifa',
  1201.         12: 'Hanafuda',
  1202.         29: 'Hex A Deck',
  1203.         13: 'Holiday',
  1204.         14: 'Movies',
  1205.         31: 'Matrix',
  1206.         15: 'Music',
  1207.         16: 'Nature',
  1208.         17: 'Operating Systems',
  1209.         19: 'People',
  1210.         20: 'Places',
  1211.         21: 'Plain',
  1212.         22: 'Products',
  1213.         18: 'Round cardsets',
  1214.         23: 'Science Fiction',
  1215.         24: 'Sports',
  1216.         27: 'Tarock',
  1217.         25: 'Vehicels',
  1218.         26: 'Video Games' }
  1219.     NATIONALITY = {
  1220.         1021: 'Australia',
  1221.         1001: 'Austria',
  1222.         1019: 'Belgium',
  1223.         1010: 'Canada',
  1224.         1011: 'China',
  1225.         1012: 'Czech Republic',
  1226.         1013: 'Denmark',
  1227.         1003: 'England',
  1228.         1004: 'France',
  1229.         1006: 'Germany',
  1230.         1014: 'Great Britain',
  1231.         1015: 'Hungary',
  1232.         1020: 'India',
  1233.         1005: 'Italy',
  1234.         1016: 'Japan',
  1235.         1002: 'Netherlands',
  1236.         1007: 'Russia',
  1237.         1008: 'Spain',
  1238.         1017: 'Sweden',
  1239.         1009: 'Switzerland',
  1240.         1018: 'USA' }
  1241.     DATE = {
  1242.         10: '1000 - 1099',
  1243.         11: '1100 - 1199',
  1244.         12: '1200 - 1299',
  1245.         13: '1300 - 1399',
  1246.         14: '1400 - 1499',
  1247.         15: '1500 - 1599',
  1248.         16: '1600 - 1699',
  1249.         17: '1700 - 1799',
  1250.         18: '1800 - 1899',
  1251.         19: '1900 - 1999',
  1252.         20: '2000 - 2099',
  1253.         21: '2100 - 2199',
  1254.         22: '2200 - 2299' }
  1255.  
  1256.  
  1257. class CardsetConfig(Struct):
  1258.     
  1259.     def __init__(_):
  1260.         Struct.__init__(_, version = 1, ext = '.gif', type = CSI.TYPE_FRENCH, ncards = -1, styles = [], year = 0, ident = '', name = '', CARDW = 0, CARDH = 0, CARDD = 0, CARD_UP_YOFFSET = 0, CARD_DOWN_YOFFSET = 0, SHADOW_XOFFSET = 0, SHADOW_YOFFSET = 0, backindex = 0, backnames = (), CARD_DX = 0, CARD_DY = 0)
  1261.  
  1262.  
  1263.  
  1264. class Cardset(Resource):
  1265.     
  1266.     def __init__(_, **kw):
  1267.         config = CardsetConfig()
  1268.         kw = apply(KwStruct, (config.__dict__,), kw)
  1269.         si = Struct(type = 0, size = 0, styles = [], nationalities = [], dates = [])
  1270.         kw = KwStruct(kw, ranks = (), suits = (), trumps = (), nbottoms = 7, nletters = 4, nshadows = 1 + 13, si = si, backname = None, dir = '')
  1271.         apply(Resource.__init__, (_,), kw.getKw())
  1272.  
  1273.     
  1274.     def getFaceCardNames(_):
  1275.         names = []
  1276.         for suit in _.suits:
  1277.             for rank in _.ranks:
  1278.                 names.append('%02d%s' % (rank + 1, suit))
  1279.             
  1280.         
  1281.         for trump in _.trumps:
  1282.             names.append('%02d%s' % (trump + 1, 'z'))
  1283.         
  1284.         if not __debug__ and len(names) == _.ncards:
  1285.             raise AssertionError
  1286.         0
  1287.         return names
  1288.  
  1289.     
  1290.     def getPreviewCardNames(_):
  1291.         names = _.getFaceCardNames()
  1292.         pnames = []
  1293.         (ranks, suits) = (_.ranks, _.suits)
  1294.         (lr, ls) = (len(ranks), len(suits))
  1295.         if lr == 0 or ls == 0:
  1296.             return (names[:16], 4)
  1297.         
  1298.         if lr >= 4:
  1299.             ls = min(ls, 4)
  1300.         
  1301.         (low_ranks, high_ranks) = (1, 3)
  1302.         for rank in range(0, low_ranks) + range(lr - high_ranks, lr):
  1303.             for suit in range(ls):
  1304.                 index = suit * len(_.ranks) + rank
  1305.                 pnames.append(names[index % len(names)])
  1306.             
  1307.         
  1308.         return (pnames, ls)
  1309.  
  1310.     
  1311.     def updateCardback(_, backname = None, backindex = None):
  1312.         if type(backname) is types.StringType:
  1313.             if backname in _.backnames:
  1314.                 backindex = _.backnames.index(backname)
  1315.             
  1316.         
  1317.         if type(backindex) is types.IntType:
  1318.             _.backindex = backindex % len(_.backnames)
  1319.         
  1320.         _.backname = _.backnames[_.backindex]
  1321.  
  1322.  
  1323.  
  1324. class CardsetManager(ResourceManager):
  1325.     
  1326.     def __init__(_):
  1327.         ResourceManager.__init__(_)
  1328.         _.registered_types = { }
  1329.         _.registered_sizes = { }
  1330.         _.registered_styles = { }
  1331.         _.registered_nationalities = { }
  1332.         _.registered_dates = { }
  1333.  
  1334.     
  1335.     def _check(_, cs):
  1336.         s = cs.type
  1337.         if not CSI.TYPE.has_key(s):
  1338.             return 0
  1339.         
  1340.         cs.si.type = s
  1341.         if s == CSI.TYPE_FRENCH:
  1342.             cs.ranks = range(13)
  1343.             cs.suits = 'cshd'
  1344.         elif s == CSI.TYPE_HANAFUDA:
  1345.             cs.ranks = range(12)
  1346.             cs.suits = 'cshd'
  1347.         elif s == CSI.TYPE_TAROCK:
  1348.             cs.nbottoms = 8
  1349.             cs.ranks = range(14)
  1350.             cs.suits = 'cshd'
  1351.             cs.trumps = range(22)
  1352.         elif s == CSI.TYPE_HEXADECK:
  1353.             cs.nbottoms = 8
  1354.             cs.ranks = range(16)
  1355.             cs.suits = 'cshd'
  1356.             cs.trumps = range(4)
  1357.         elif s == CSI.TYPE_MUGHAL_GANJIFA:
  1358.             cs.nbottoms = 11
  1359.             cs.ranks = range(12)
  1360.             cs.suits = 'abcdefgh'
  1361.         elif s == CSI.TYPE_NAVAGRAHA_GANJIFA:
  1362.             return 0
  1363.             cs.nbottoms = 12
  1364.             cs.ranks = range(12)
  1365.             cs.suits = 'abcdefghi'
  1366.         elif s == CSI.TYPE_DASHAVATARA_GANJIFA:
  1367.             cs.nbottoms = 13
  1368.             cs.ranks = range(12)
  1369.             cs.suits = 'abcdefghij'
  1370.         elif s == CSI.TYPE_TRUMP_ONLY:
  1371.             return 0
  1372.             cs.nbottoms = 7
  1373.             cs.ranks = ()
  1374.             cs.suits = ''
  1375.             cs.trumps = range(cs.ncards)
  1376.         else:
  1377.             return 0
  1378.         return 1
  1379.  
  1380.     
  1381.     def register(_, cs):
  1382.         if not _._check(cs):
  1383.             return None
  1384.         
  1385.         cs.ncards = len(cs.ranks) * len(cs.suits) + len(cs.trumps)
  1386.         cs.name = cs.name[:25]
  1387.         if not None if cs.si.size <= cs.si.size else cs.si.size <= 5:
  1388.             (CW, CH) = (cs.CARDW, cs.CARDH)
  1389.             if CW <= 55 and CH <= 72:
  1390.                 cs.si.size = CSI.SIZE_TINY
  1391.             elif CW <= 60 and CH <= 85:
  1392.                 cs.si.size = CSI.SIZE_SMALL
  1393.             elif CW <= 75 and CH <= 105:
  1394.                 cs.si.size = CSI.SIZE_MEDIUM
  1395.             elif CW <= 90 and CH <= 125:
  1396.                 cs.si.size = CSI.SIZE_LARGE
  1397.             else:
  1398.                 cs.si.size = CSI.SIZE_XLARGE
  1399.         
  1400.         keys = cs.styles[:]
  1401.         cs.si.styles = tuple(filter((lambda s: CSI.STYLE.has_key(s)), keys))
  1402.         for s in cs.si.styles:
  1403.             _.registered_styles[s] = _.registered_styles.get(s, 0) + 1
  1404.         
  1405.         cs.si.nationalities = tuple(filter((lambda s: CSI.NATIONALITY.has_key(s)), keys))
  1406.         for s in cs.si.nationalities:
  1407.             _.registered_nationalities[s] = _.registered_nationalities.get(s, 0) + 1
  1408.         
  1409.         keys = (cs.year / 100,)
  1410.         cs.si.dates = tuple(filter((lambda s: CSI.DATE.has_key(s)), keys))
  1411.         for s in cs.si.dates:
  1412.             _.registered_dates[s] = _.registered_dates.get(s, 0) + 1
  1413.         
  1414.         s = cs.si.type
  1415.         _.registered_types[s] = _.registered_types.get(s, 0) + 1
  1416.         s = cs.si.size
  1417.         _.registered_sizes[s] = _.registered_sizes.get(s, 0) + 1
  1418.         cs.updateCardback()
  1419.         ResourceManager.register(_, cs)
  1420.  
  1421.  
  1422.  
  1423. class Tile(Resource):
  1424.     
  1425.     def __init__(_, **kw):
  1426.         kw = KwStruct(kw, color = None, text_color = '#000000')
  1427.         apply(Resource.__init__, (_,), kw.getKw())
  1428.  
  1429.  
  1430.  
  1431. class TileManager(ResourceManager):
  1432.     pass
  1433.  
  1434.  
  1435. class Sample(Resource):
  1436.     
  1437.     def __init__(_, **kw):
  1438.         kw = KwStruct(kw, volume = -1)
  1439.         apply(Resource.__init__, (_,), kw.getKw())
  1440.  
  1441.  
  1442.  
  1443. class SampleManager(ResourceManager):
  1444.     pass
  1445.  
  1446.  
  1447. class Music(Sample):
  1448.     pass
  1449.  
  1450.  
  1451. class MusicManager(SampleManager):
  1452.     pass
  1453.  
  1454.  
  1455. class GI:
  1456.     GC_FRENCH = CSI.TYPE_FRENCH
  1457.     GC_HANAFUDA = CSI.TYPE_HANAFUDA
  1458.     GC_TAROCK = CSI.TYPE_TAROCK
  1459.     GC_MAHJONGG = CSI.TYPE_MAHJONGG
  1460.     GC_HEXADECK = CSI.TYPE_HEXADECK
  1461.     GC_MUGHAL_GANJIFA = CSI.TYPE_MUGHAL_GANJIFA
  1462.     GC_NAVAGRAHA_GANJIFA = CSI.TYPE_NAVAGRAHA_GANJIFA
  1463.     GC_DASHAVATARA_GANJIFA = CSI.TYPE_DASHAVATARA_GANJIFA
  1464.     GC_TRUMP_ONLY = CSI.TYPE_TRUMP_ONLY
  1465.     GT_1DECK_TYPE = 0
  1466.     GT_2DECK_TYPE = 1
  1467.     GT_3DECK_TYPE = 2
  1468.     GT_4DECK_TYPE = 3
  1469.     GT_BAKERS_DOZEN = 4
  1470.     GT_BELEAGUERED_CASTLE = 5
  1471.     GT_CANFIELD = 6
  1472.     GT_DASHAVATARA_GANJIFA = 7
  1473.     GT_FAN_TYPE = 8
  1474.     GT_FORTY_THIEVES = 9
  1475.     GT_FREECELL = 10
  1476.     GT_GOLF = 11
  1477.     GT_GYPSY = 12
  1478.     GT_HANAFUDA = 13
  1479.     GT_HEXADECK = 14
  1480.     GT_KLONDIKE = 15
  1481.     GT_MAHJONGG = 16
  1482.     GT_MATRIX = 17
  1483.     GT_MEMORY = 18
  1484.     GT_MONTANA = 19
  1485.     GT_MUGHAL_GANJIFA = 20
  1486.     GT_NAPOLEON = 21
  1487.     GT_NAVAGRAHA_GANJIFA = 22
  1488.     GT_NUMERICA = 23
  1489.     GT_PAIRING_TYPE = 24
  1490.     GT_POKER_TYPE = 25
  1491.     GT_PUZZLE_TYPE = 26
  1492.     GT_RAGLAN = 27
  1493.     GT_ROW_TYPE = 28
  1494.     GT_SIMPLE_TYPE = 29
  1495.     GT_SPIDER = 30
  1496.     GT_TAROCK = 31
  1497.     GT_TERRACE = 32
  1498.     GT_YUKON = 33
  1499.     GT_BETA = 1 << 12
  1500.     GT_CHILDREN = 1 << 13
  1501.     GT_CONTRIB = 1 << 14
  1502.     GT_HIDDEN = 1 << 15
  1503.     GT_OPEN = 1 << 16
  1504.     GT_ORIGINAL = 1 << 17
  1505.     GT_POPULAR = 1 << 18
  1506.     GT_RELAXED = 1 << 19
  1507.     GT_SCORE = 1 << 20
  1508.     GT_SEPARATE_DECKS = 1 << 21
  1509.     GT_XORIGINAL = 1 << 22
  1510.     SELECT_GAME_BY_TYPE = (("Baker's Dozen type", (lambda gi, gt = GT_BAKERS_DOZEN: gi.si.game_type == gt)), ('Beleaguered Castle type', (lambda gi, gt = GT_BELEAGUERED_CASTLE: gi.si.game_type == gt)), ('Canfield type', (lambda gi, gt = GT_CANFIELD: gi.si.game_type == gt)), ('Fan type', (lambda gi, gt = GT_FAN_TYPE: gi.si.game_type == gt)), ('Forty Thieves type', (lambda gi, gt = GT_FORTY_THIEVES: gi.si.game_type == gt)), ('FreeCell type', (lambda gi, gt = GT_FREECELL: gi.si.game_type == gt)), ('Golf type', (lambda gi, gt = GT_GOLF: gi.si.game_type == gt)), ('Gypsy type', (lambda gi, gt = GT_GYPSY: gi.si.game_type == gt)), ('Klondike type', (lambda gi, gt = GT_KLONDIKE: gi.si.game_type == gt)), ('Montana type', (lambda gi, gt = GT_MONTANA: gi.si.game_type == gt)), ('Napoleon type', (lambda gi, gt = GT_NAPOLEON: gi.si.game_type == gt)), ('Numerica type', (lambda gi, gt = GT_NUMERICA: gi.si.game_type == gt)), ('Pairing type', (lambda gi, gt = GT_PAIRING_TYPE: gi.si.game_type == gt)), ('Raglan type', (lambda gi, gt = GT_RAGLAN: gi.si.game_type == gt)), ('Simple games', (lambda gi, gt = GT_SIMPLE_TYPE: gi.si.game_type == gt)), ('Spider type', (lambda gi, gt = GT_SPIDER: gi.si.game_type == gt)), ('Terrace type', (lambda gi, gt = GT_TERRACE: gi.si.game_type == gt)), ('Yukon type', (lambda gi, gt = GT_YUKON: gi.si.game_type == gt)), ('One-Deck games', (lambda gi, gt = GT_1DECK_TYPE: gi.si.game_type == gt)), ('Two-Deck games', (lambda gi, gt = GT_2DECK_TYPE: gi.si.game_type == gt)), ('Three-Deck games', (lambda gi, gt = GT_3DECK_TYPE: gi.si.game_type == gt)), ('Four-Deck games', (lambda gi, gt = GT_4DECK_TYPE: gi.si.game_type == gt)))
  1511.     SELECT_SPECIAL_GAME_BY_TYPE = (('Dashavatara Ganjifa type', (lambda gi, gt = GT_DASHAVATARA_GANJIFA: gi.si.game_type == gt)), ('Hanafuda type', (lambda gi, gt = GT_HANAFUDA: gi.si.game_type == gt)), ('Hex A Deck type', (lambda gi, gt = GT_HEXADECK: gi.si.game_type == gt)), ('Matrix type', (lambda gi, gt = GT_MATRIX: gi.si.game_type == gt)), ('Mughal Ganjifa type', (lambda gi, gt = GT_MUGHAL_GANJIFA: gi.si.game_type == gt)), ('Navagraha Ganjifa type', (lambda gi, gt = GT_NAVAGRAHA_GANJIFA: gi.si.game_type == gt)), ('Memory type', (lambda gi, gt = GT_MEMORY: gi.si.game_type == gt)), ('Poker type', (lambda gi, gt = GT_POKER_TYPE: gi.si.game_type == gt)), ('Puzzle type', (lambda gi, gt = GT_PUZZLE_TYPE: gi.si.game_type == gt)), ('Tarock type', (lambda gi, gt = GT_TAROCK: gi.si.game_type == gt)))
  1512.     SELECT_ORIGINAL_GAME_BY_TYPE = (('French type', (lambda gi, gf = GT_ORIGINAL, gt = (GT_HANAFUDA, GT_HEXADECK, GT_MUGHAL_GANJIFA, GT_NAVAGRAHA_GANJIFA, GT_DASHAVATARA_GANJIFA, GT_TAROCK): if gi.si.game_flags & gf:
  1513. passgi.si.game_type not in gt)), ('Ganjifa type', (lambda gi, gf = GT_ORIGINAL, gt = (GT_MUGHAL_GANJIFA, GT_NAVAGRAHA_GANJIFA, GT_DASHAVATARA_GANJIFA): if gi.si.game_flags & gf:
  1514. passgi.si.game_type in gt)), ('Hanafuda type', (lambda gi, gf = GT_ORIGINAL, gt = GT_HANAFUDA: if gi.si.game_flags & gf:
  1515. passgi.si.game_type == gt)), ('Hex A Deck type', (lambda gi, gf = GT_ORIGINAL, gt = GT_HEXADECK: if gi.si.game_flags & gf:
  1516. passgi.si.game_type == gt)), ('Tarock type', (lambda gi, gf = GT_ORIGINAL, gt = GT_TAROCK: if gi.si.game_flags & gf:
  1517. passgi.si.game_type == gt)))
  1518.     SELECT_CONTRIB_GAME_BY_TYPE = (('French type', (lambda gi, gf = GT_CONTRIB, gt = (GT_HANAFUDA, GT_HEXADECK, GT_MUGHAL_GANJIFA, GT_NAVAGRAHA_GANJIFA, GT_DASHAVATARA_GANJIFA, GT_TAROCK): if gi.si.game_flags & gf:
  1519. passgi.si.game_type not in gt)), ('Ganjifa type', (lambda gi, gf = GT_CONTRIB, gt = (GT_MUGHAL_GANJIFA, GT_NAVAGRAHA_GANJIFA, GT_DASHAVATARA_GANJIFA): if gi.si.game_flags & gf:
  1520. passgi.si.game_type in gt)), ('Hanafuda type', (lambda gi, gf = GT_CONTRIB, gt = GT_HANAFUDA: if gi.si.game_flags & gf:
  1521. passgi.si.game_type == gt)), ('Hex A Deck type', (lambda gi, gf = GT_CONTRIB, gt = GT_HEXADECK: if gi.si.game_flags & gf:
  1522. passgi.si.game_type == gt)), ('Tarock type', (lambda gi, gf = GT_CONTRIB, gt = GT_TAROCK: if gi.si.game_flags & gf:
  1523. passgi.si.game_type == gt)))
  1524.     PROTECTED_GAMES = {
  1525.         22: 106,
  1526.         32: 901,
  1527.         52: 903,
  1528.         72: 115,
  1529.         75: 126,
  1530.         82: 901,
  1531.         262: 105,
  1532.         902: 88,
  1533.         904: 68 }
  1534.     GAMES_BY_COMPATIBILITY = (('Atari ST Patience', (1, 3, 4, 7, 12, 14, 15, 16, 17, 39)), ('Gnome AisleRiot', (2, 8, 11, 19, 27, 29, 33, 34, 35, 40, 41, 42, 43, 58, 59, 92, 93, 94, 95, 96, 100, 105, 111, 112, 113, 130, 200, 201)), ('KDE Patience', (2, 7, 8, 18, 256, 903)), ('xpat2', (1, 2, 8, 9, 11, 31, 54, 63, 89, 105, 901, 256, 345, 903)))
  1535.     GAMES_BY_PYSOL_VERSION = (('1.00', (1, 2, 3, 4)), ('1.01', (5, 6)), ('1.02', (7, 8, 9)), ('1.03', (10, 11, 12, 13)), ('1.10', (14,)), ('1.11', (15, 16, 17)), ('2.00', (256, 257)), ('2.01', (258, 259, 260, 261)), ('2.02', (105,)), ('2.90', (18, 19, 20, 21, 106, 23, 24, 25, 26, 27, 28, 29, 30, 31, 901, 33, 34, 35, 36)), ('2.99', (37,)), ('3.00', (38, 39, 40, 41, 42, 43, 45, 46, 47, 48, 49, 50, 51, 903, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 115, 73, 74, 126, 76, 77, 78, 79, 80, 81, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 107, 108)), ('3.10', (109, 110, 111, 112, 113, 114, 116, 117, 118, 119, 120, -121, -122, 123, 124, 125, 127)), ('3.20', (128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 345, 346, 347, 348, 349, 350, 351, 352)), ('3.21', (143, 144)), ('3.30', (145, 146, 147, 148, 149, 150, 151)), ('3.40', (152, 153, 154)), ('4.00', (157, 158, 159, 160, 161, 162, 163, 164)), ('4.20', (165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178)), ('4.30', (179, 180, 181, 182, 183, 184)), ('4.41', (185, 186, -187, -188, -189, -190, -191, -192, 193, -194, 195, 196, -197, -198, 199)), ('4.60', (200, 201, 202, 203, 204, 205)))
  1536.     _CHILDREN_GAMES = [
  1537.         16,
  1538.         33,
  1539.         55,
  1540.         90,
  1541.         91,
  1542.         96,
  1543.         97,
  1544.         176,
  1545.         903]
  1546.     _OPEN_GAMES = [
  1547.         5,
  1548.         6,
  1549.         8,
  1550.         9,
  1551.         26,
  1552.         31,
  1553.         45,
  1554.         46,
  1555.         50,
  1556.         53,
  1557.         63,
  1558.         64,
  1559.         77,
  1560.         85,
  1561.         86,
  1562.         96,
  1563.         116,
  1564.         117,
  1565.         118,
  1566.         258]
  1567.     _POPULAR_GAMES = [
  1568.         1,
  1569.         2,
  1570.         7,
  1571.         8,
  1572.         11,
  1573.         12,
  1574.         13,
  1575.         14,
  1576.         19,
  1577.         31,
  1578.         36,
  1579.         38,
  1580.         105,
  1581.         158,
  1582.         345,
  1583.         903]
  1584.     
  1585.     def assertGI(_, manager = None):
  1586.         for key, games in _.GAMES_BY_COMPATIBILITY:
  1587.             games = filter((lambda id: id >= 0), games)
  1588.             print '%-20s: %d games' % (key, len(games))
  1589.             for id in games[:]:
  1590.                 if not __debug__ and type(id) is types.IntType:
  1591.                     raise AssertionError
  1592.                 0
  1593.             
  1594.         
  1595.         all_games = []
  1596.         for key, games in _.GAMES_BY_PYSOL_VERSION:
  1597.             games = filter((lambda id: id >= 0), games)
  1598.             print '%s: %3d games, %3d total' % (key, len(games), len(all_games) + len(games))
  1599.             for id in games[:]:
  1600.                 if not __debug__ and type(id) is types.IntType:
  1601.                     raise AssertionError
  1602.                 0
  1603.                 if _.PROTECTED_GAMES.get(id):
  1604.                     print id
  1605.                     if not __debug__ and 0:
  1606.                         raise AssertionError, id
  1607.                     _.GAMES_BY_PYSOL_VERSION
  1608.                 
  1609.                 if id in all_games:
  1610.                     print id
  1611.                     if not __debug__ and 0:
  1612.                         raise AssertionError, id
  1613.                 
  1614.                 if manager and not manager.get(id):
  1615.                     print id
  1616.                     if not __debug__ and 0:
  1617.                         raise AssertionError, id
  1618.                 
  1619.             
  1620.             all_games.extend(list(games))
  1621.         
  1622.         print 'total:', len(all_games), 'games'
  1623.         if manager:
  1624.             g = manager.getGamesIdSortedById()
  1625.             g = filter((lambda id, m = manager: m.get(id).plugin == 0), g)
  1626.             g = list(g)[:]
  1627.             for id in all_games:
  1628.                 g.remove(id)
  1629.             
  1630.             if g:
  1631.                 print 'WARNING: games without version:', g
  1632.             
  1633.         
  1634.  
  1635.  
  1636.  
  1637. class GameInfoException(Exception):
  1638.     pass
  1639.  
  1640.  
  1641. class GameInfo(Struct):
  1642.     
  1643.     def __init__(_, id, gameclass, name, game_type, decks, redeals, si = { }, category = 0, short_name = None, altnames = None, suits = range(4), ranks = range(13), trumps = (), rules_filename = None):
  1644.         ncards = decks * (len(suits) * len(ranks) + len(trumps))
  1645.         game_flags = game_type & ~1023
  1646.         game_type = game_type & 1023
  1647.         if os.name == 'mac':
  1648.             name = latin1_to_ascii(name)
  1649.         
  1650.         if not short_name:
  1651.             short_name = name
  1652.         
  1653.         if type(altnames) is types.StringType:
  1654.             altnames = (altnames,)
  1655.         
  1656.         if not altnames:
  1657.             altnames = ()
  1658.         
  1659.         if not None if id <= id else id <= 999999:
  1660.             raise GameInfoException, name + ': invalid game ID ' + str(id)
  1661.         
  1662.         if not None if decks <= decks else decks <= 4:
  1663.             raise GameInfoException, name + ': invalid number of decks ' + str(id)
  1664.         
  1665.         if not name or not None if len(short_name) <= len(short_name) else len(short_name) <= 25:
  1666.             raise GameInfoException, name + ': invalid game name'
  1667.         
  1668.         if GI.PROTECTED_GAMES.get(id):
  1669.             raise GameInfoException, name + ': protected game ID ' + str(id)
  1670.         
  1671.         for f, l in ((GI.GT_CHILDREN, GI._CHILDREN_GAMES), (GI.GT_OPEN, GI._OPEN_GAMES), (GI.GT_POPULAR, GI._POPULAR_GAMES)):
  1672.             if game_flags & f and id not in l:
  1673.                 l.append(id)
  1674.             elif not (game_flags & f) and id in l:
  1675.                 game_flags = game_flags | f
  1676.             
  1677.         
  1678.         if not None if category <= category else category <= 9:
  1679.             if game_type == GI.GT_HANAFUDA:
  1680.                 category = GI.GC_HANAFUDA
  1681.             elif game_type == GI.GT_TAROCK:
  1682.                 category = GI.GC_TAROCK
  1683.             elif game_type == GI.GT_MAHJONGG:
  1684.                 category = GI.GC_MAHJONGG
  1685.             elif game_type == GI.GT_HEXADECK:
  1686.                 category = GI.GC_HEXADECK
  1687.             elif game_type == GI.GT_MUGHAL_GANJIFA:
  1688.                 category = GI.GC_MUGHAL_GANJIFA
  1689.             elif game_type == GI.GT_NAVAGRAHA_GANJIFA:
  1690.                 category = GI.GC_NAVAGRAHA_GANJIFA
  1691.             elif game_type == GI.GT_DASHAVATARA_GANJIFA:
  1692.                 category = GI.GC_DASHAVATARA_GANJIFA
  1693.             else:
  1694.                 category = GI.GC_FRENCH
  1695.         
  1696.         gi_si = Struct(game_type = game_type, game_flags = game_flags, decks = decks, redeals = redeals, ncards = ncards)
  1697.         gi_si.update(si)
  1698.         Struct.__init__(_, id = id, gameclass = gameclass, name = name, short_name = short_name, altnames = tuple(altnames), decks = decks, redeals = redeals, ncards = ncards, category = category, suits = tuple(suits), ranks = tuple(ranks), trumps = tuple(trumps), si = gi_si, rules_filename = rules_filename, plugin = 0)
  1699.  
  1700.  
  1701.  
  1702. class GameManager:
  1703.     
  1704.     def __init__(_):
  1705.         _._GameManager__selected_key = -1
  1706.         _._GameManager__games = { }
  1707.         _._GameManager__gamenames = { }
  1708.         _._GameManager__games_by_id = None
  1709.         _._GameManager__games_by_name = None
  1710.         _._GameManager__games_by_short_name = None
  1711.         _._GameManager__games_by_altname = None
  1712.         _._GameManager__all_games = { }
  1713.         _._GameManager__all_gamenames = { }
  1714.         _.loading_plugin = 0
  1715.         _.registered_game_types = { }
  1716.  
  1717.     
  1718.     def getSelected(_):
  1719.         return _._GameManager__selected_key
  1720.  
  1721.     
  1722.     def setSelected(_, gameid):
  1723.         if not __debug__ and _._GameManager__all_games.has_key(gameid):
  1724.             raise AssertionError
  1725.         _._GameManager__selected_key = gameid
  1726.  
  1727.     
  1728.     def get(_, key):
  1729.         return _._GameManager__all_games.get(key)
  1730.  
  1731.     
  1732.     def register(_, gi):
  1733.         if not isinstance(gi, GameInfo):
  1734.             raise GameInfoException, 'wrong GameInfo class'
  1735.         
  1736.         gi.plugin = _.loading_plugin
  1737.         if _._GameManager__all_games.has_key(gi.id):
  1738.             raise GameInfoException, 'duplicate game ID ' + str(gi.id)
  1739.         
  1740.         if _._GameManager__all_gamenames.has_key(gi.name):
  1741.             raise GameInfoException, 'duplicate game name ' + str(gi.id) + ': ' + game.name
  1742.         
  1743.         if gi.si.game_flags & GI.GT_XORIGINAL:
  1744.             return None
  1745.         
  1746.         if 1:
  1747.             if gi.id <= gi.id:
  1748.                 pass
  1749.             elif gi.id <= 236:
  1750.                 return None
  1751.             
  1752.         _._GameManager__all_games[gi.id] = gi
  1753.         _._GameManager__all_gamenames[gi.name] = gi
  1754.         for n in gi.altnames:
  1755.             _._GameManager__all_gamenames[n] = gi
  1756.         
  1757.  
  1758.     
  1759.     def getGamesIdSortedById(_):
  1760.         if _._GameManager__games_by_id is None:
  1761.             l = _._GameManager__games.keys()
  1762.             l.sort()
  1763.             _._GameManager__games_by_id = tuple(l)
  1764.         
  1765.         return _._GameManager__games_by_id
  1766.  
  1767.     
  1768.     def getGamesIdSortedByName(_):
  1769.         return _._GameManager__games_by_name
  1770.  
  1771.     
  1772.     def getGamesIdSortedByShortName(_):
  1773.         if _._GameManager__games_by_name is None:
  1774.             _.getGamesIdSortedByName()
  1775.         
  1776.         return _._GameManager__games_by_short_name
  1777.  
  1778.     
  1779.     def getGamesTuplesSortedByAlternateName(_):
  1780.         if _._GameManager__games_by_name is None:
  1781.             _.getGamesIdSortedByName()
  1782.         
  1783.         return _._GameManager__games_by_altname
  1784.  
  1785.  
  1786. GAME_DB = GameManager()
  1787.  
  1788. def registerGame(gameinfo):
  1789.     GAME_DB.register(gameinfo)
  1790.     return gameinfo
  1791.  
  1792.  
  1793. def loadGame(modname, filename, plugin = 1):
  1794.     GAME_DB.loading_plugin = plugin
  1795.     if os.name == 'nt':
  1796.         return None
  1797.     
  1798.     execfile(filename, globals(), globals())
  1799.  
  1800.  
  1801. class AbstractAudioClient:
  1802.     
  1803.     def __init__(_):
  1804.         _.server = None
  1805.         _.audiodev = None
  1806.         _.connected = 0
  1807.         _.app = None
  1808.         _.file_cache = { }
  1809.         _.sample_priority = -1
  1810.         _.sample_loop = 0
  1811.         _.music_priority = -1
  1812.         _.music_loop = 0
  1813.  
  1814.     
  1815.     def __del__(_):
  1816.         _.destroy()
  1817.  
  1818.     
  1819.     def startServer(_):
  1820.         pass
  1821.  
  1822.     
  1823.     def connectServer(_, app):
  1824.         if not __debug__ and app:
  1825.             raise AssertionError
  1826.         _.app = app
  1827.         if _.server is not None:
  1828.             
  1829.             try:
  1830.                 if _._connectServer():
  1831.                     _.connected = 1
  1832.             except:
  1833.                 if traceback:
  1834.                     traceback.print_exc()
  1835.                 
  1836.                 _.destroy()
  1837.  
  1838.         
  1839.  
  1840.     
  1841.     def destroy(_):
  1842.         if _.audiodev is not None:
  1843.             
  1844.             try:
  1845.                 _._destroy()
  1846.             except:
  1847.                 pass
  1848.  
  1849.         
  1850.         _.server = None
  1851.         _.audiodev = None
  1852.         _.connected = 0
  1853.         _.app = None
  1854.  
  1855.     
  1856.     def stopAll(_):
  1857.         _.stopSamples()
  1858.         _.stopMusic()
  1859.  
  1860.     
  1861.     def playSample(_, name, priority = 0, loop = 0, volume = -1):
  1862.         if _.audiodev is None and not (_.app) or not (_.app.opt.sound):
  1863.             return 0
  1864.         
  1865.         if priority <= _.sample_priority and _.sample_loop:
  1866.             return 0
  1867.         
  1868.         obj = _.app.sample_manager.getByName(name)
  1869.         if not obj or not (obj.absname):
  1870.             return 0
  1871.         
  1872.         
  1873.         try:
  1874.             if _._playSample(obj.absname, priority, loop, volume):
  1875.                 _.sample_priority = priority
  1876.                 _.sample_loop = loop
  1877.                 return 1
  1878.         except:
  1879.             if traceback:
  1880.                 traceback.print_exc()
  1881.             
  1882.  
  1883.         return 0
  1884.  
  1885.     
  1886.     def stopSamples(_):
  1887.         if _.audiodev is None:
  1888.             return None
  1889.         
  1890.         
  1891.         try:
  1892.             _._stopSamples()
  1893.         except:
  1894.             if traceback:
  1895.                 traceback.print_exc()
  1896.             
  1897.  
  1898.         _.sample_priority = -1
  1899.         _.sample_loop = 0
  1900.  
  1901.     
  1902.     def stopSamplesLoop(_):
  1903.         if _.audiodev is None:
  1904.             return None
  1905.         
  1906.         
  1907.         try:
  1908.             _._stopSamplesLoop()
  1909.         except:
  1910.             if traceback:
  1911.                 traceback.print_exc()
  1912.             
  1913.  
  1914.         _.sample_priority = -1
  1915.         _.sample_loop = 0
  1916.  
  1917.     
  1918.     def playMusic(_, basename, priority = 0, loop = 0, volume = -1):
  1919.         if _.audiodev is None and not (_.app) or not (_.app.opt.sound):
  1920.             return 0
  1921.         
  1922.         if priority <= _.music_priority and _.music_loop:
  1923.             return 0
  1924.         
  1925.         obj = _.app.music_manager.getByBasename(basename)
  1926.         if not obj or not (obj.absname):
  1927.             return 0
  1928.         
  1929.         
  1930.         try:
  1931.             if _._playMusic(obj.absname, priority, loop, volume):
  1932.                 _.music_priority = priority
  1933.                 _.music_loop = loop
  1934.                 return 1
  1935.         except:
  1936.             if traceback:
  1937.                 traceback.print_exc()
  1938.             
  1939.  
  1940.         return 0
  1941.  
  1942.     
  1943.     def stopMusic(_):
  1944.         if _.audiodev is None:
  1945.             return None
  1946.         
  1947.         
  1948.         try:
  1949.             _._stopMusic()
  1950.         except:
  1951.             if traceback:
  1952.                 traceback.print_exc()
  1953.             
  1954.  
  1955.         _.music_priority = -1
  1956.         _.music_loop = 0
  1957.  
  1958.     
  1959.     def _connectServer(_):
  1960.         return 0
  1961.  
  1962.     
  1963.     def _destroy(_):
  1964.         pass
  1965.  
  1966.     
  1967.     def _playSample(_, name, priority, loop):
  1968.         return 0
  1969.  
  1970.     
  1971.     def _stopSamples(_):
  1972.         pass
  1973.  
  1974.     
  1975.     def _stopSamplesLoop(_):
  1976.         _._stopSamples()
  1977.  
  1978.     
  1979.     def _playMusic(_, name, priority, loop, volume):
  1980.         return 0
  1981.  
  1982.     
  1983.     def _stopMusic(_):
  1984.         pass
  1985.  
  1986.     
  1987.     def getMusicInfo(_):
  1988.         return -1
  1989.  
  1990.     
  1991.     def playContinuousMusic(_, music_list):
  1992.         pass
  1993.  
  1994.     
  1995.     def playNextMusic(_):
  1996.         pass
  1997.  
  1998.     
  1999.     def updateSettings(_):
  2000.         pass
  2001.  
  2002.  
  2003.  
  2004. class PysolSoundServerModuleClient(AbstractAudioClient):
  2005.     
  2006.     def startServer(_):
  2007.         
  2008.         try:
  2009.             _.audiodev = pysolsoundserver
  2010.             _.audiodev.init()
  2011.             _.server = 1
  2012.         except:
  2013.             if traceback:
  2014.                 traceback.print_exc()
  2015.             
  2016.             _.server = None
  2017.             _.audiodev = None
  2018.  
  2019.  
  2020.     
  2021.     def cmd(_, cmd):
  2022.         _.audiodev.cmd(cmd)
  2023.  
  2024.     
  2025.     def _connectServer(_):
  2026.         _.cmd('protocol 4')
  2027.         if 0 and _.app.debug:
  2028.             _.cmd('debug 1')
  2029.         
  2030.         return 1
  2031.  
  2032.     
  2033.     def _destroy(_):
  2034.         _.audiodev.exit()
  2035.  
  2036.     
  2037.     def _playSample(_, filename, priority, loop, volume):
  2038.         _.cmd("playwav '%s' %d %d %d %d" % (filename, -1, priority, loop, volume))
  2039.         return 1
  2040.  
  2041.     
  2042.     def _stopSamples(_):
  2043.         _.cmd('stopwav')
  2044.  
  2045.     
  2046.     def _stopSamplesLoop(_):
  2047.         _.cmd('stopwavloop')
  2048.  
  2049.     
  2050.     def _playMusic(_, filename, priority, loop, volume):
  2051.         _.cmd("playmus '%s' %d %d %d %d" % (filename, -1, priority, loop, volume))
  2052.         return 1
  2053.  
  2054.     
  2055.     def _stopMusic(_):
  2056.         _.cmd('stopmus')
  2057.  
  2058.     
  2059.     def getMusicInfo(_):
  2060.         if _.audiodev:
  2061.             return _.audiodev.getMusicInfo()
  2062.         
  2063.         return -1
  2064.  
  2065.     
  2066.     def playContinuousMusic(_, music_list):
  2067.         if _.audiodev is None or not (_.app):
  2068.             return None
  2069.         
  2070.         
  2071.         try:
  2072.             for music in music_list:
  2073.                 pass
  2074.             
  2075.             _.cmd('startqueue')
  2076.         except:
  2077.             if traceback:
  2078.                 traceback.print_exc()
  2079.             
  2080.  
  2081.  
  2082.     
  2083.     def playNextMusic(_):
  2084.         _.cmd('nextmus')
  2085.  
  2086.     
  2087.     def updateSettings(_):
  2088.         if _.audiodev is None or not (_.app):
  2089.             return None
  2090.         
  2091.         (s, m) = (0, 0)
  2092.         if _.app.opt.sound:
  2093.             s = _.app.opt.sound_sample_volume
  2094.             m = _.app.opt.sound_music_volume
  2095.         
  2096.         
  2097.         try:
  2098.             _.cmd('setwavvol %d' % s)
  2099.             _.cmd('setmusvol %d' % m)
  2100.         except:
  2101.             if traceback:
  2102.                 traceback.print_exc()
  2103.             
  2104.  
  2105.  
  2106.  
  2107.  
  2108. class Win32AudioClient(AbstractAudioClient):
  2109.     
  2110.     def startServer(_):
  2111.         
  2112.         try:
  2113.             import winsound
  2114.             _.audiodev = winsound
  2115.             del winsound
  2116.             _.server = 0
  2117.         except:
  2118.             _.server = None
  2119.             _.audiodev = None
  2120.  
  2121.  
  2122.     
  2123.     def _playSample(_, filename, priority, loop, volume):
  2124.         a = _.audiodev
  2125.         flags = a.SND_FILENAME | a.SND_NODEFAULT | a.SND_NOWAIT | a.SND_ASYNC
  2126.         if loop:
  2127.             flags = flags | a.SND_LOOP
  2128.         
  2129.         if priority <= _.sample_priority:
  2130.             flags = flags | a.SND_NOSTOP
  2131.         
  2132.         
  2133.         try:
  2134.             a.PlaySound(filename, flags)
  2135.             return 1
  2136.         except:
  2137.             pass
  2138.  
  2139.         return 0
  2140.  
  2141.     
  2142.     def _stopSamples(_):
  2143.         a = _.audiodev
  2144.         flags = a.SND_NODEFAULT | a.SND_PURGE
  2145.         a.PlaySound(None, flags)
  2146.  
  2147.  
  2148.  
  2149. def Misc__destroy(_):
  2150.     pass
  2151.  
  2152.  
  2153. def Canvas__tag_bind(_, tagOrId, sequence = None, func = None, add = None):
  2154.     return _._bind((_._w, 'bind', tagOrId), sequence, func, add)
  2155.  
  2156.  
  2157. def Wm__wm_state(_, newstate = None):
  2158.     return _.tk.call('wm', 'state', _._w, newstate)
  2159.  
  2160.  
  2161. def Text__xview_moveto(_, fraction):
  2162.     return _.tk.call(_._w, 'xview', 'moveto', fraction)
  2163.  
  2164.  
  2165. def Text__xview_scroll(_, number, what):
  2166.     return _.tk.call(_._w, 'xview', 'scroll', number, what)
  2167.  
  2168.  
  2169. def Text__yview_moveto(_, fraction):
  2170.     return _.tk.call(_._w, 'yview', 'moveto', fraction)
  2171.  
  2172.  
  2173. def Text__yview_scroll(_, number, what):
  2174.     return _.tk.call(_._w, 'yview', 'scroll', number, what)
  2175.  
  2176. Tkinter.Misc.destroy = Misc__destroy
  2177. Tkinter.Canvas.tag_bind = Canvas__tag_bind
  2178. Tkinter.Wm.wm_state = Wm__wm_state
  2179. Tkinter.Wm.state = Wm__wm_state
  2180. Tkinter.Text.xview_moveto = Text__xview_moveto
  2181. Tkinter.Text.xview_scroll = Text__xview_scroll
  2182. Tkinter.Text.yview_moveto = Text__yview_moveto
  2183. Tkinter.Text.yview_scroll = Text__yview_scroll
  2184.  
  2185. def CanvasItem__bbox(_):
  2186.     return _.canvas.bbox(_.id)
  2187.  
  2188.  
  2189. def Group__bbox(_):
  2190.     return _.canvas.bbox(_.id)
  2191.  
  2192.  
  2193. def CanvasItem__bind(_, sequence = None, command = None, add = None):
  2194.     return _.canvas.tag_bind(_.id, sequence, command, add)
  2195.  
  2196.  
  2197. def Group__bind(_, sequence = None, command = None, add = None):
  2198.     return _.canvas.tag_bind(_.id, sequence, command, add)
  2199.  
  2200.  
  2201. def CanvasItem__unbind(_, sequence, funcid = None):
  2202.     return _.canvas.tag_unbind(_.id, sequence, funcid)
  2203.  
  2204.  
  2205. def Group__unbind(_, sequence, funcid = None):
  2206.     return _.canvas.tag_unbind(_.id, sequence, funcid)
  2207.  
  2208.  
  2209. def CanvasItem__tkraise(_, abovethis = None):
  2210.     return _.canvas.tag_raise(_.id, abovethis)
  2211.  
  2212.  
  2213. def CanvasItem__lower(_, belowthis = None):
  2214.     return _.canvas.tag_lower(_.id, belowthis)
  2215.  
  2216.  
  2217. def Group__tkraise(_, abovethis = None):
  2218.     return _.canvas.tag_raise(_.id, abovethis)
  2219.  
  2220.  
  2221. def Group__lower(_, belowthis = None):
  2222.     return _.canvas.tag_lower(_.id, belowthis)
  2223.  
  2224. Canvas.CanvasItem.bbox = CanvasItem__bbox
  2225. Canvas.Group.bbox = Group__bbox
  2226. Canvas.CanvasItem.bind = CanvasItem__bind
  2227. Canvas.Group.bind = Group__bind
  2228. Canvas.CanvasItem.unbind = CanvasItem__unbind
  2229. Canvas.Group.unbind = Group__unbind
  2230. Canvas.CanvasItem.tkraise = CanvasItem__tkraise
  2231. Canvas.CanvasItem.lower = CanvasItem__lower
  2232. Canvas.Group.tkraise = Group__tkraise
  2233. Canvas.Group.lower = Group__lower
  2234.  
  2235. def CallWrapper____call__(_, *args):
  2236.     if _.subst:
  2237.         args = apply(_.subst, args)
  2238.     
  2239.     return apply(_.func, args)
  2240.  
  2241. Tkinter.CallWrapper.__call__ = CallWrapper____call__
  2242. tkname = 'tk'
  2243. tkversion = (8, 0, 0, 0)
  2244.  
  2245. try:
  2246.     m = string.split(str(Tkinter._tkinter.TK_VERSION), '.')
  2247.     if m:
  2248.         m = map(int, m) + 4 * [
  2249.             0]
  2250.         tkversion = tuple(m[:4])
  2251.     
  2252.     del m
  2253. except:
  2254.     pass
  2255.  
  2256. TK_DASH_PATCH = 0
  2257. EVENT_HANDLED = 'break'
  2258. EVENT_PROPAGATE = None
  2259. CURSOR_DRAG = 'hand1'
  2260. CURSOR_WATCH = 'watch'
  2261. ANCHOR_CENTER = Tkinter.CENTER
  2262. ANCHOR_N = Tkinter.N
  2263. ANCHOR_NW = Tkinter.NW
  2264. ANCHOR_NE = Tkinter.NE
  2265. ANCHOR_S = Tkinter.S
  2266. ANCHOR_SW = Tkinter.SW
  2267. ANCHOR_SE = Tkinter.SE
  2268. ANCHOR_W = Tkinter.W
  2269. ANCHOR_E = Tkinter.E
  2270.  
  2271. def wm_withdraw(window):
  2272.     window.wm_withdraw()
  2273.  
  2274.  
  2275. def wm_deiconify(window):
  2276.     if os.name == 'nt':
  2277.         pass
  2278.     need_fix = tkversion < (8, 3, 0, 0)
  2279.     if need_fix:
  2280.         
  2281.         try:
  2282.             window.wm_iconify()
  2283.             window.update_idletasks()
  2284.         except Tkinter.TclError:
  2285.             pass
  2286.  
  2287.     
  2288.     window.wm_deiconify()
  2289.  
  2290.  
  2291. def wm_map(window, maximized = 0):
  2292.     if window.wm_state() != 'iconic':
  2293.         if maximized and os.name == 'nt':
  2294.             window.wm_state('zoomed')
  2295.         else:
  2296.             wm_deiconify(window)
  2297.     
  2298.  
  2299.  
  2300. def wm_set_icon(window, filename):
  2301.     if not filename:
  2302.         return None
  2303.     
  2304.     if os.name == 'posix':
  2305.         window.wm_iconbitmap('@' + filename)
  2306.         window.wm_iconmask('@' + filename)
  2307.     
  2308.  
  2309.  
  2310. def wm_parse_geometry(geometry):
  2311.     m = re.search('^(\\d+)x(\\d+)\\+([\\-]?\\d+)\\+([\\-]?\\d+)$', geometry)
  2312.     if m:
  2313.         return tuple(map(int, m.groups()))
  2314.     
  2315.     raise Tkinter.TclError, 'invalid geometry ' + str(geometry)
  2316.  
  2317.  
  2318. def setTransient(window, parent, relx = None, rely = None, expose = 1):
  2319.     window.wm_withdraw()
  2320.     window.wm_group(parent)
  2321.     if os.name == 'nt':
  2322.         pass
  2323.     need_fix = tkversion < (8, 3, 0, 0)
  2324.     if need_fix:
  2325.         window.wm_geometry('+%d+%d' % (-10000, -10000))
  2326.         if expose and parent is not None:
  2327.             window.wm_iconify()
  2328.         
  2329.     
  2330.     if parent and parent.wm_state() != 'withdrawn':
  2331.         window.wm_transient(parent)
  2332.     
  2333.     window.update_idletasks()
  2334.     (x, y) = __getWidgetXY(window, parent, relx = relx, rely = rely)
  2335.     if need_fix:
  2336.         if expose:
  2337.             wm_deiconify(window)
  2338.         
  2339.         window.wm_geometry('+%d+%d' % (x, y))
  2340.     else:
  2341.         window.wm_geometry('+%d+%d' % (x, y))
  2342.         if expose:
  2343.             window.wm_deiconify()
  2344.         
  2345.  
  2346.  
  2347. def makeToplevel(parent, title = None, class_ = None):
  2348.     if class_:
  2349.         window = Tkinter.Toplevel(parent, class_ = class_)
  2350.     else:
  2351.         window = Tkinter.Toplevel(parent)
  2352.     if title:
  2353.         window.wm_title(title)
  2354.         window.wm_iconname(title)
  2355.     
  2356.     return window
  2357.  
  2358. makeHelpToplevel = makeToplevel
  2359.  
  2360. def __getWidgetXY(widget, parent, relx = None, rely = None, w_width = None, w_height = None):
  2361.     if w_width is None:
  2362.         w_width = widget.winfo_reqwidth()
  2363.     
  2364.     if w_height is None:
  2365.         w_height = widget.winfo_reqheight()
  2366.     
  2367.     s_width = widget.winfo_screenwidth()
  2368.     s_height = widget.winfo_screenheight()
  2369.     m_x = m_y = 0
  2370.     (m_width, m_height) = (s_width, s_height)
  2371.     if parent and parent.winfo_ismapped():
  2372.         m_x = m_y = None
  2373.         if os.name == 'nt':
  2374.             
  2375.             try:
  2376.                 g = parent.wm_geometry()
  2377.                 (m_width, m_height, m_x, m_y) = wm_parse_geometry(g)
  2378.                 if parent.wm_state() == 'zoomed':
  2379.                     m_x = m_y = 0
  2380.             except:
  2381.                 pass
  2382.  
  2383.         
  2384.         if m_x is None:
  2385.             m_x = parent.winfo_x()
  2386.             m_y = parent.winfo_y()
  2387.             m_width = parent.winfo_width()
  2388.             m_height = parent.winfo_height()
  2389.             if relx is None:
  2390.                 relx = 0.5
  2391.             
  2392.             if rely is None:
  2393.                 rely = 0.3
  2394.             
  2395.         elif relx is None:
  2396.             relx = 0.5
  2397.         
  2398.         if rely is None:
  2399.             rely = 0.5
  2400.         
  2401.         m_x = max(m_x, 0)
  2402.         m_y = max(m_y, 0)
  2403.     elif relx is None:
  2404.         relx = 0.5
  2405.     
  2406.     if rely is None:
  2407.         rely = 0.3
  2408.     
  2409.     x = m_x + int((m_width - w_width) * relx)
  2410.     y = m_y + int((m_height - w_height) * rely)
  2411.     if x < 0:
  2412.         x = 0
  2413.     elif x + w_width + 32 > s_width:
  2414.         x = max(0, (s_width - w_width) / 2)
  2415.     
  2416.     if y < 0:
  2417.         y = 0
  2418.     elif y + w_height + 32 > s_height:
  2419.         y = max(0, (s_height - w_height) / 2)
  2420.     
  2421.     return (x, y)
  2422.  
  2423. __mfx_bindings = { }
  2424.  
  2425. def bind(widget, sequence, func, add = None):
  2426.     if not __debug__ and callable(func):
  2427.         raise AssertionError
  2428.     if sequence == 'WM_DELETE_WINDOW':
  2429.         funcid = widget._register(func)
  2430.         widget.tk.call('wm', 'protocol', widget._w, sequence, funcid)
  2431.     elif add is None:
  2432.         funcid = widget.bind(sequence, func)
  2433.     else:
  2434.         funcid = widget.bind(sequence, func, add)
  2435.     k = id(widget)
  2436.     if __mfx_bindings.has_key(k):
  2437.         __mfx_bindings[k].append((sequence, funcid))
  2438.     else:
  2439.         __mfx_bindings[k] = [
  2440.             (sequence, funcid)]
  2441.  
  2442.  
  2443. def unbind_destroy(widget):
  2444.     if widget is None:
  2445.         return None
  2446.     
  2447.     k = id(widget)
  2448.  
  2449.  
  2450. def after(widget, ms, func, *args):
  2451.     timer = apply(widget.after, (ms, func) + args)
  2452.     command = widget._tclCommands[-1]
  2453.     return (timer, command, widget)
  2454.  
  2455.  
  2456. def after_idle(widget, func, *args):
  2457.     return apply(after, (widget, 'idle', func) + args)
  2458.  
  2459.  
  2460. def after_cancel(t):
  2461.     if t is not None:
  2462.         t[2].after_cancel(t[0])
  2463.         
  2464.         try:
  2465.             t[2].deletecommand(t[1])
  2466.         except Tkinter.TclError:
  2467.             pass
  2468.  
  2469.     
  2470.  
  2471. getFont_cache = { }
  2472.  
  2473. def getFont(name, cardw = 0):
  2474.     key = (name, cardw)
  2475.     font = getFont_cache.get(key)
  2476.     if font:
  2477.         return font
  2478.     
  2479.     font = ('Helvetica', '14')
  2480.     if os.name == 'nt':
  2481.         font = ('MS Sans Serif', '10')
  2482.     elif os.name == 'mac':
  2483.         font = ('Chicago', '12')
  2484.     
  2485.     if name in ('canvas', 'canvas_small', 'small', 'tree_small'):
  2486.         font = ('Helvetica', '10')
  2487.         if os.name == 'nt':
  2488.             font = ('MS Sans Serif', '8')
  2489.         
  2490.     elif name in ('canvas_large',):
  2491.         font = ('Helvetica', '-18')
  2492.         if os.name == 'nt':
  2493.             font = ('Arial', '14')
  2494.         
  2495.     elif name in ('canvas_card',):
  2496.         if cardw >= 71:
  2497.             font = ('Helvetica', '-18')
  2498.             if os.name == 'nt':
  2499.                 font = ('Arial', '14')
  2500.             
  2501.         elif cardw >= 57:
  2502.             font = ('Helvetica', '-16')
  2503.         else:
  2504.             font = ('Helvetica', '-14')
  2505.     elif name in ('canvas_fixed',):
  2506.         font = ('Courier', '-12')
  2507.         if os.name == 'nt':
  2508.             font = ('Courier New', '9')
  2509.         elif os.name == 'mac':
  2510.             font = ('Monaco', '9')
  2511.         
  2512.     elif name in ('fixed',):
  2513.         font = ('Courier', '-14')
  2514.         if os.name == 'nt':
  2515.             font = ('Courier New', '10')
  2516.         elif os.name == 'mac':
  2517.             font = ('Courier', '12')
  2518.         
  2519.     elif not (name in ('default',)):
  2520.         pass
  2521.     
  2522.     getFont_cache[key] = font
  2523.     return font
  2524.  
  2525.  
  2526. def loadImage(file):
  2527.     return Tkinter.PhotoImage(file = file)
  2528.  
  2529.  
  2530. def copyImage(image, x, y, width, height):
  2531.     dest = Tkinter.PhotoImage(width = width, height = height)
  2532.     if not __debug__ and dest.width() == width:
  2533.         raise AssertionError
  2534.     if not __debug__ and dest.height() == height:
  2535.         raise AssertionError
  2536.     dest.blank()
  2537.     image.tk.call(dest, 'copy', image.name, '-from', x, y, x + width, y + height)
  2538.     if not __debug__ and dest.width() == width:
  2539.         raise AssertionError
  2540.     if not __debug__ and dest.height() == height:
  2541.         raise AssertionError
  2542.     return dest
  2543.  
  2544.  
  2545. def fillImage(image, fill, outline = None):
  2546.     if not fill and not outline:
  2547.         return None
  2548.     
  2549.     width = image.width()
  2550.     height = image.height()
  2551.     ow = 1
  2552.     if width <= 2 * ow or height <= 2 * ow:
  2553.         if not fill:
  2554.             pass
  2555.         fill = outline
  2556.         outline = None
  2557.     
  2558.     if not outline:
  2559.         f = (fill,) * width
  2560.         f = (f,) * height
  2561.         if not __debug__ and len(f) == height:
  2562.             raise AssertionError
  2563.         image.put(f)
  2564.     elif not fill:
  2565.         l = ((outline,) * width,)
  2566.         for y in range(0, ow):
  2567.             image.put(l, (0, y))
  2568.         
  2569.         for y in range(height - ow, height):
  2570.             image.put(l, (0, y))
  2571.         
  2572.         p = ((outline,) * ow,)
  2573.         for y in range(ow, height - ow):
  2574.             image.put(p, (0, y))
  2575.             image.put(p, (width - ow, y))
  2576.         
  2577.     else:
  2578.         l1 = (outline,) * width
  2579.         l2 = (outline,) * ow + (fill,) * (width - 2 * ow) + (outline,) * ow
  2580.         f = (l1,) * ow + (l2,) * (height - 2 * ow) + (l1,) * ow
  2581.         if not __debug__ and len(f) == height:
  2582.             raise AssertionError
  2583.         range(ow, height - ow)
  2584.         image.put(f)
  2585.  
  2586.  
  2587. def createImage(width, height, fill, outline = None):
  2588.     image = Tkinter.PhotoImage(width = width, height = height)
  2589.     if not __debug__ and image.width() == width:
  2590.         raise AssertionError
  2591.     if not __debug__ and image.height() == height:
  2592.         raise AssertionError
  2593.     image.blank()
  2594.     fillImage(image, fill, outline)
  2595.     return image
  2596.  
  2597.  
  2598. class MfxCanvasGroup(Canvas.Group):
  2599.     
  2600.     def __init__(_, canvas, tag = None):
  2601.         Canvas.Group.__init__(_, canvas = canvas, tag = tag)
  2602.         if not __debug__ and not _.canvas.items.has_key(_.id):
  2603.             raise AssertionError
  2604.         _.canvas.items[_.id] = _
  2605.  
  2606.     
  2607.     def addtag(_, tag, option = 'withtag'):
  2608.         _.canvas.addtag(tag, option, _.id)
  2609.  
  2610.     
  2611.     def delete(_):
  2612.         del _.canvas.items[_.id]
  2613.         Canvas.Group.delete(_)
  2614.  
  2615.     
  2616.     def gettags(_):
  2617.         return _.canvas.tk.splitlist(_._do('gettags'))
  2618.  
  2619.  
  2620.  
  2621. class MfxCanvasImage(Canvas.ImageItem):
  2622.     
  2623.     def moveTo(_, x, y):
  2624.         c = _.coords()
  2625.         _.move(x - int(c[0]), y - int(c[1]))
  2626.  
  2627.  
  2628. MfxCanvasLine = Canvas.Line
  2629. MfxCanvasRectangle = Canvas.Rectangle
  2630.  
  2631. class MfxCanvasText(Canvas.CanvasText):
  2632.     
  2633.     def __init__(_, canvas, x, y, preview = -1, **kw):
  2634.         if preview < 0:
  2635.             preview = canvas.preview
  2636.         
  2637.         if preview > 1:
  2638.             return None
  2639.         
  2640.         if not kw.has_key('fill'):
  2641.             kw['fill'] = canvas._text_color
  2642.         
  2643.         apply(Canvas.CanvasText.__init__, (_, canvas, x, y), kw)
  2644.         _.text_format = None
  2645.         canvas._text_items.append(_)
  2646.  
  2647.  
  2648.  
  2649. class MfxCanvas(Tkinter.Canvas):
  2650.     
  2651.     def __init__(_, *args, **kw):
  2652.         apply(Tkinter.Canvas.__init__, (_,) + args, kw)
  2653.         _.preview = 0
  2654.         _.items = { }
  2655.         _._MfxCanvas__tileimage = None
  2656.         _._MfxCanvas__tiles = []
  2657.         _._MfxCanvas__topimage = None
  2658.         _._MfxCanvas__tops = []
  2659.         _._text_color = '#000000'
  2660.         _._text_items = []
  2661.  
  2662.     
  2663.     def _x_create(_, itemType, *args, **kw):
  2664.         return Tkinter.Canvas._create(_, itemType, args, kw)
  2665.  
  2666.     
  2667.     def _create(_, itemType, args, kw):
  2668.         id = Tkinter.Canvas._create(_, itemType, args, kw)
  2669.         if _._MfxCanvas__tops:
  2670.             _.tk.call(_._w, 'lower', id, _._MfxCanvas__tops[0])
  2671.         
  2672.         return id
  2673.  
  2674.     
  2675.     def tag_raise(_, id, aboveThis = None):
  2676.         if aboveThis is None and _._MfxCanvas__tops:
  2677.             _.tk.call(_._w, 'lower', id, _._MfxCanvas__tops[0])
  2678.         else:
  2679.             _.tk.call(_._w, 'raise', id, aboveThis)
  2680.  
  2681.     
  2682.     def tag_lower(_, id, belowThis = None):
  2683.         if belowThis is None and _._MfxCanvas__tiles:
  2684.             _.tk.call(_._w, 'raise', id, _._MfxCanvas__tiles[-1])
  2685.         else:
  2686.             _.tk.call(_._w, 'lower', id, belowThis)
  2687.  
  2688.     
  2689.     def deleteAllItems(_):
  2690.         _._text_items = []
  2691.         for id in _.items.keys():
  2692.             if not __debug__ and not (id in _._MfxCanvas__tiles):
  2693.                 raise AssertionError
  2694.             0
  2695.             unbind_destroy(_.items[id])
  2696.             _.items[id].delete()
  2697.         
  2698.         if not __debug__ and _.items == { }:
  2699.             raise AssertionError
  2700.         _.items.keys()
  2701.  
  2702.     
  2703.     def findCard(_, stack, event):
  2704.         return -1
  2705.  
  2706.     
  2707.     def setTextColor(_, color):
  2708.         pass
  2709.  
  2710.     
  2711.     def setTile(_, image):
  2712.         
  2713.         try:
  2714.             if image and type(image) is types.StringType:
  2715.                 image = Tkinter.PhotoImage(file = image)
  2716.         except Tkinter.TclError:
  2717.             return 0
  2718.  
  2719.         for id in _._MfxCanvas__tiles:
  2720.             _.delete(id)
  2721.         
  2722.         _._MfxCanvas__tiles = []
  2723.         _._MfxCanvas__tileimage = image
  2724.         (iw, ih) = (image.width(), image.height())
  2725.         sw = max(_.winfo_screenwidth(), 1024)
  2726.         sh = max(_.winfo_screenheight(), 768)
  2727.         for x in range(0, sw - 1, iw):
  2728.             for y in range(0, sh - 1, ih):
  2729.                 id = _._x_create('image', x, y, image = image, anchor = 'nw')
  2730.                 _.tag_lower(id)
  2731.                 _._MfxCanvas__tiles.append(id)
  2732.             
  2733.         
  2734.         return 1
  2735.  
  2736.     
  2737.     def setTopImage(_, image, cw = 0, ch = 0):
  2738.         
  2739.         try:
  2740.             if image and type(image) is types.StringType:
  2741.                 image = Tkinter.PhotoImage(file = image)
  2742.         except Tkinter.TclError:
  2743.             return 0
  2744.  
  2745.         for id in _._MfxCanvas__tops:
  2746.             _.delete(id)
  2747.         
  2748.         _._MfxCanvas__tops = []
  2749.         _._MfxCanvas__topimage = image
  2750.         (iw, ih) = (image.width(), image.height())
  2751.         if cw <= 0:
  2752.             cw = int(_.cget('width'))
  2753.         
  2754.         if ch <= 0:
  2755.             ch = int(_.cget('height'))
  2756.         
  2757.         x = (cw - iw) / 2
  2758.         y = (ch - ih) / 2
  2759.         id = _._x_create('image', x, y, image = image, anchor = 'nw')
  2760.         _.tk.call(_._w, 'raise', id)
  2761.         _._MfxCanvas__tops.append(id)
  2762.         return 1
  2763.  
  2764.     
  2765.     def _bind(_, what, sequence, func, add, needcleanup = 1):
  2766.         funcid = _._register(func, _._substitute, needcleanup)
  2767.         if not add and '+':
  2768.             pass
  2769.         cmd = '%sif {"[%s %s]" == "break"} break\n' % ('', funcid, '%x %y')
  2770.         _.tk.call(what + (sequence, cmd))
  2771.         return funcid
  2772.  
  2773.     
  2774.     def _substitute(_, *args):
  2775.         e = Tkinter.Event()
  2776.         e.x = int(args[0])
  2777.         e.y = int(args[1])
  2778.         return (e,)
  2779.  
  2780.  
  2781.  
  2782. class MfxCheckMenuItem(Tkinter.BooleanVar):
  2783.     
  2784.     def __init__(_, menubar, path = None):
  2785.         Tkinter.BooleanVar.__init__(_)
  2786.  
  2787.     
  2788.     def set(_, value):
  2789.         if not value or value == 'false':
  2790.             value = 0
  2791.         
  2792.         if __debug__:
  2793.             if type(value) is types.IntType:
  2794.                 if value <= value:
  2795.                     pass
  2796.                 elif not value <= 1:
  2797.                     raise AssertionError
  2798.         Tkinter.BooleanVar.set(_, value)
  2799.  
  2800.  
  2801.  
  2802. class MfxRadioMenuItem(Tkinter.IntVar):
  2803.     
  2804.     def __init__(_, menubar, path = None):
  2805.         Tkinter.IntVar.__init__(_)
  2806.  
  2807.     
  2808.     def set(_, value):
  2809.         if __debug__:
  2810.             if not type(value) is types.IntType and 0 <= value:
  2811.                 raise AssertionError
  2812.         Tkinter.IntVar.set(_, value)
  2813.  
  2814.  
  2815.  
  2816. class MfxRoot(Tkinter.Tk):
  2817.     
  2818.     def __init__(_, **kw):
  2819.         apply(Tkinter.Tk.__init__, (_,), kw)
  2820.         _.app = None
  2821.  
  2822.     
  2823.     def connectApp(_, app):
  2824.         _.app = app
  2825.  
  2826.     
  2827.     def busyUpdate(_):
  2828.         game = None
  2829.         if _.app:
  2830.             game = _.app.game
  2831.         
  2832.         if not game:
  2833.             _.update()
  2834.         else:
  2835.             old_busy = game.busy
  2836.             game.busy = 1
  2837.             if game.canvas:
  2838.                 game.canvas.update()
  2839.             
  2840.             _.update()
  2841.             game.busy = old_busy
  2842.  
  2843.     
  2844.     def mainquit(_):
  2845.         _.after_idle(_.quit)
  2846.  
  2847.     
  2848.     def screenshot(_, filename):
  2849.         pass
  2850.  
  2851.     
  2852.     def setCursor(_, cursor):
  2853.         pass
  2854.  
  2855.     
  2856.     def sleep(_, seconds):
  2857.         time.sleep(seconds)
  2858.  
  2859.     
  2860.     def update(_):
  2861.         Tkinter.Tk.update(_)
  2862.  
  2863.     
  2864.     def wmDeleteWindow(_):
  2865.         if _.app and _.app.menubar:
  2866.             _.app.menubar.mQuit()
  2867.         
  2868.  
  2869.  
  2870.  
  2871. class _ToplevelDialog:
  2872.     img = None
  2873.     
  2874.     def __init__(_, parent, title = '', resizable = 0, default = -1):
  2875.         _.parent = parent
  2876.         _.status = 0
  2877.         _.button = default
  2878.         _.timer = None
  2879.         _.top = makeToplevel(parent, title = title)
  2880.         _.top.wm_resizable(resizable, resizable)
  2881.         bind(_.top, 'WM_DELETE_WINDOW', _.wmDeleteWindow)
  2882.         if _.img is None:
  2883.             _ToplevelDialog.img = (Tkinter.PhotoImage(data = '\nR0lGODlhIAAgAPICAH8AAH9/f7+/v/8AAP///wAAAAAAAAAAACH5BAEAAAIALAAAAAAgACAAAAPZ\nKLos8PC1SRccOGNYO9AgCHTNE57ZQwoA6mZAB7z0AFRA/QLBBISEFyEECDAAIYISpSSEAAwAqKkM\nUQkgQGABCF011xAgEBAAUFfMFQUICACu69UFCAgAL7nyBSADaHI0AGQANHI0AGQAL3oELwBkAC6N\nSi4AZAAoVxhXKABkAgAhVxpXIQBkAQIAIFchVyAAqQIAIU0oTSEAZAsAIQQvBCEAqQwAOi4AqQEM\nAMgoAKkTAM8gAKkVD9UPyx0BAMgAywErARAoEOMrC2QREeMB6w3w9AHyFfUrCQA7'), Tkinter.PhotoImage(data = '\nR0lGODlhIAAgAPIDAAAAAAAA/39/f7+/v////wAAAAAAAAAAACH5BAEAAAMALAAAAAAgACAAAAPU\nOLo88vC1SRccJOsBa32DJo7D4zHCMK7jIJyDwA5BHQws4AksYds9AEXQI/xqRUBDMCgOfoPiAMAQ\nFK8sgEAhGGC/mgFAMBBcjzUsQDAQfI9YAOR7xAIg3yMWAPkesQAQA1hHVwMADwMCWEdXABADAgNX\nR0UDABAKAj1oNiwAEAwAYEUAEQ0ApCsAERQAA6oEAwARHgAAA18DtxEwArcAAysDwBECMAoQwAMa\nAMYPyAwRABoAxtEVAgAaAA/YyAAZAA/fyAAEAA/lyLcP68gQ78gPMAkAOw=='), Tkinter.PhotoImage(data = '\nR0lGODlhIAAgAPIDAAAAAAAA/39/f7+/v////wAAAAAAAAAAACH5BAEAAAMALAAAAAAgACAAAAPp\nOLo88vC1SRccJOsBa32DJo7D4zHCMK7jIJyDwM4j4AnsEOz8wAIUAWsQGGR4gQEL0BAMWDsRkjUA\nMAQzZAY5AwgUgsEMSRggZwOAYCCgDXYDApJGAAgGArpmwNMDIHoZPIEAEIEEOwOBABCHO4cAEAOH\nhwMADwMCegM7A3oAEAMCA3RIdAMAEAoCdEh0ABAMADQDOwM0ABENAJQrABEUAAO9BAMAER4AAAOB\nA8oRMALKAAMrA9MRAjAKENMDGgDZD9sMEQAaANnkFQIAGgAP69sAGQAP8tsABAAP+NvKD/xtgyBw\n2wMYCQAAOw=='), Tkinter.PhotoImage(data = '\nR0lGODlhIAAgAPIDAAAAAH9/AH9/f7+/v///AAAAAAAAAAAAACH5BAEAAAMALAAAAAAgACAAAAPu\nOLq88S3KqQIhA9Atw70DIHDkEHwXIAglFaDXAKytFMAXsNZNgF8DwIq3CPwugBVxEDheBoAVMfAZ\nAK6DD2DFC6Cu4M8AsKoFUOArCrBqBWBpAGoAWJUCsDgMsCIFcHEwAwArHAE4AWABOAArGwE/A2AD\nOAMAKxQBR2BHACsTAU4BAAFHAwArEgFOAwADTgArEQFOFwC1AwArDQG1vhcAKwwBvgNXA04DACsL\nAb5ptQArCgG/abUDACsDAb8DVwO+ACsDAb/nFwArAgHovwMA6wMB7k4DAOsCCgFx/f79+QQwCEiw\noEEBEQ4qJKggAQA7'))
  2884.         
  2885.  
  2886.     
  2887.     def mainloop(_, focus = None, timeout = 0):
  2888.         bind(_.top, '<Escape>', _.mCancel)
  2889.         if focus is not None:
  2890.             focus.focus()
  2891.         
  2892.         setTransient(_.top, _.parent)
  2893.         
  2894.         try:
  2895.             _.top.grab_set()
  2896.         except Tkinter.TclError:
  2897.             pass
  2898.  
  2899.         if timeout > 0:
  2900.             _.timer = after(_.top, timeout, _.mTimeout)
  2901.         
  2902.         
  2903.         try:
  2904.             _.top.mainloop()
  2905.         except SystemExit:
  2906.             pass
  2907.  
  2908.         _.destroy()
  2909.  
  2910.     
  2911.     def destroy(_):
  2912.         after_cancel(_.timer)
  2913.         unbind_destroy(_.top)
  2914.         
  2915.         try:
  2916.             _.top.wm_withdraw()
  2917.         except:
  2918.             if traceback:
  2919.                 traceback.print_exc()
  2920.             
  2921.  
  2922.         
  2923.         try:
  2924.             _.top.destroy()
  2925.         except:
  2926.             if traceback:
  2927.                 traceback.print_exc()
  2928.             
  2929.  
  2930.         destruct(_.top)
  2931.         if _.parent:
  2932.             
  2933.             try:
  2934.                 if hasattr(_.parent, 'busyUpdate'):
  2935.                     _.parent.busyUpdate()
  2936.                 else:
  2937.                     _.parent.update()
  2938.             except:
  2939.                 if traceback:
  2940.                     traceback.print_exc()
  2941.                 
  2942.  
  2943.         
  2944.         _.top = None
  2945.         _.parent = None
  2946.  
  2947.     
  2948.     def getDefaultFont(_):
  2949.         return getFont('default')
  2950.  
  2951.     
  2952.     def wmDeleteWindow(_, *event):
  2953.         _.status = 1
  2954.         raise SystemExit
  2955.  
  2956.     
  2957.     def mCancel(_, *event):
  2958.         _.status = 1
  2959.         raise SystemExit
  2960.  
  2961.     
  2962.     def mTimeout(_, *event):
  2963.         _.status = 2
  2964.         raise SystemExit
  2965.  
  2966.  
  2967.  
  2968. class MfxDialog(_ToplevelDialog):
  2969.     
  2970.     def __init__(_, parent, title, **kw):
  2971.         kw = _.initKw(kw)
  2972.         _ToplevelDialog.__init__(_, parent, title, kw.resizable, kw.default)
  2973.         (top_frame, bottom_frame) = _.createFrames(kw)
  2974.         _.createBitmaps(top_frame, kw)
  2975.         _.button = kw.default
  2976.         msg = Tkinter.Label(top_frame, text = kw.text, justify = kw.justify, width = kw.width, font = kw.font)
  2977.         msg.pack(fill = Tkinter.BOTH, expand = 1, padx = kw.padx, pady = kw.pady)
  2978.         focus = _.createButtons(bottom_frame, kw)
  2979.         _.mainloop(focus, kw.timeout)
  2980.  
  2981.     
  2982.     def initKw(_, kw):
  2983.         kw = KwStruct(kw, timeout = 0, resizable = 0, text = '', justify = 'center', strings = ('OK',), default = 0, width = 0, font = _.getDefaultFont(), buttonfont = _.getDefaultFont(), padx = 20, pady = 20, bitmap = None, bitmap_side = 'left', bitmap_padx = 10, bitmap_pady = 20, image = None, image_side = 'left', image_padx = 10, image_pady = 20)
  2984.         sw = 2 * (len(kw.strings) > 1)
  2985.         kwdefault(kw.__dict__, separatorwidth = sw)
  2986.         return kw
  2987.  
  2988.     
  2989.     def createFrames(_, kw):
  2990.         frame = Tkinter.Frame(_.top)
  2991.         frame.pack(side = Tkinter.TOP, fill = Tkinter.BOTH, expand = 1)
  2992.         if kw.separatorwidth > 0:
  2993.             separator = Tkinter.Frame(_.top, relief = 'sunken', height = kw.separatorwidth, width = kw.separatorwidth, borderwidth = kw.separatorwidth / 2)
  2994.             separator.pack(side = Tkinter.TOP, fill = Tkinter.X)
  2995.         
  2996.         bot = Tkinter.Frame(_.top)
  2997.         bot.pack(side = Tkinter.BOTTOM, fill = Tkinter.BOTH, ipady = 3)
  2998.         return (frame, bot)
  2999.  
  3000.     
  3001.     def createBitmaps(_, frame, kw):
  3002.         bm = [
  3003.             'error',
  3004.             'info',
  3005.             'questhead',
  3006.             'warning']
  3007.         if kw.bitmap in bm:
  3008.             b = Tkinter.Label(frame, image = _.img[bm.index(kw.bitmap)])
  3009.             b.pack(side = kw.bitmap_side, padx = kw.bitmap_padx, pady = kw.bitmap_pady)
  3010.         elif kw.bitmap:
  3011.             b = Tkinter.Label(frame, bitmap = kw.bitmap)
  3012.             b.pack(side = kw.bitmap_side, padx = kw.bitmap_padx, pady = kw.bitmap_pady)
  3013.         elif kw.image:
  3014.             b = Tkinter.Label(frame, image = kw.image)
  3015.             b.pack(side = kw.image_side, padx = kw.image_padx, pady = kw.image_pady)
  3016.         
  3017.  
  3018.     
  3019.     def createButtons(_, frame, kw):
  3020.         button = column = -1
  3021.         (padx, pady) = (kw.get('buttonpadx', 10), kw.get('buttonpady', 10))
  3022.         focus = None
  3023.         max_len = 6
  3024.         for s in kw.strings:
  3025.             if s:
  3026.                 s = re.sub('[\\s\\.\\,]', '', s)
  3027.                 max_len = max(max_len, len(s))
  3028.             
  3029.         
  3030.         for s in kw.strings:
  3031.             if s is None:
  3032.                 continue
  3033.             
  3034.             if button < 0:
  3035.                 b = Tkinter.Button(frame, text = s, font = kw.buttonfont, state = 'disabled')
  3036.                 button = xbutton
  3037.             else:
  3038.                 b = Tkinter.Button(frame, text = s, font = kw.buttonfont, default = 'normal', command = (lambda _ = _, button = button: _.mDone(button)))
  3039.                 if button == kw.default:
  3040.                     focus = b
  3041.                     focus.config(default = 'active')
  3042.                 
  3043.             l = len(s)
  3044.             if 1 and l < max_len:
  3045.                 l = l + (max_len - l) / 2
  3046.                 b.config(width = l)
  3047.             
  3048.             column = column + 1
  3049.             b.grid_configure(column = column, row = 0, sticky = 'ew', padx = padx, pady = pady)
  3050.             b.grid_columnconfigure(column)
  3051.         
  3052.         if focus is not None:
  3053.             
  3054.             l = lambda event = None, _ = _, button = kw.default: _.mDone(button)
  3055.             bind(_.top, '<Return>', l)
  3056.             bind(_.top, '<KP_Enter>', l)
  3057.         
  3058.         return focus
  3059.  
  3060.     
  3061.     def mDone(_, button):
  3062.         _.button = button
  3063.         raise SystemExit
  3064.  
  3065.  
  3066.  
  3067. class MfxExceptionDialog(MfxDialog):
  3068.     
  3069.     def __init__(_, parent, ex, title = 'Error', **kw):
  3070.         kw = KwStruct(kw, bitmap = 'error')
  3071.         text = str(kw.get('text', ''))
  3072.         if text and text[-1] != '\n':
  3073.             text = text + '\n'
  3074.         
  3075.         text = text + '\n'
  3076.         if isinstance(ex, EnvironmentError) and ex.filename is not None:
  3077.             t = '[Errno %s] %s:\n%s' % (ex.errno, ex.strerror, repr(ex.filename))
  3078.         else:
  3079.             t = str(ex)
  3080.         kw.text = text + t
  3081.         apply(MfxDialog.__init__, (_, parent, title), kw.getKw())
  3082.  
  3083.  
  3084.  
  3085. class MfxSimpleSlider(MfxDialog):
  3086.     
  3087.     def __init__(_, parent, title, label, value, from_, to, resolution, **kw):
  3088.         kw = _.initKw(kw)
  3089.         _ToplevelDialog.__init__(_, parent, title, kw.resizable, kw.default)
  3090.         (top_frame, bottom_frame) = _.createFrames(kw)
  3091.         _.createBitmaps(top_frame, kw)
  3092.         _.value = value
  3093.         _.var = Tkinter.DoubleVar()
  3094.         _.var.set(value)
  3095.         slider = Tkinter.Scale(top_frame, from_ = from_, to = to, resolution = resolution, orient = Tkinter.HORIZONTAL, length = '3i', label = label, variable = _.var, takefocus = 0)
  3096.         slider.pack(side = Tkinter.TOP, padx = kw.padx, pady = kw.pady)
  3097.         focus = _.createButtons(bottom_frame, kw)
  3098.         _.mainloop(focus, kw.timeout)
  3099.  
  3100.     
  3101.     def initKw(_, kw):
  3102.         kw = KwStruct(kw, strings = ('OK', 'Cancel'), default = 0, separatorwidth = 0)
  3103.         return MfxDialog.initKw(_, kw)
  3104.  
  3105.     
  3106.     def mDone(_, button):
  3107.         _.button = button
  3108.         _.value = _.var.get()
  3109.         raise SystemExit
  3110.  
  3111.  
  3112.  
  3113. class MfxSimpleEntry(MfxSimpleSlider):
  3114.     
  3115.     def __init__(_, parent, title, label, value, **kw):
  3116.         kw = _.initKw(kw)
  3117.         _ToplevelDialog.__init__(_, parent, title, kw.resizable, kw.default)
  3118.         (top_frame, bottom_frame) = _.createFrames(kw)
  3119.         _.createBitmaps(top_frame, kw)
  3120.         _.value = value
  3121.         if label:
  3122.             label = Tkinter.Label(top_frame, text = label, takefocus = 0)
  3123.             label.pack(pady = 5)
  3124.         
  3125.         w = kw.get('e_width', 0)
  3126.         _.var = Tkinter.Entry(top_frame, exportselection = 1, width = w)
  3127.         _.var.insert(0, value)
  3128.         _.var.pack(side = Tkinter.TOP, padx = kw.padx, pady = kw.pady)
  3129.         focus = _.createButtons(bottom_frame, kw)
  3130.         focus = _.var
  3131.         _.mainloop(focus, kw.timeout)
  3132.  
  3133.  
  3134.  
  3135. class MfxTooltip:
  3136.     
  3137.     def __init__(_, widget):
  3138.         _.widget = widget
  3139.         _.text = None
  3140.         _.timer = None
  3141.         _.tooltip = None
  3142.         _.label = None
  3143.         _.bindings = []
  3144.         _.bindings.append(_.widget.bind('<Enter>', _._enter))
  3145.         _.bindings.append(_.widget.bind('<Leave>', _._leave))
  3146.         _.bindings.append(_.widget.bind('<ButtonPress>', _._leave))
  3147.         _.time = 1000
  3148.         _.relief = Tkinter.SOLID
  3149.         _.justify = Tkinter.LEFT
  3150.         _.fg = '#000000'
  3151.         _.bg = '#ffffe0'
  3152.         _.xoffset = 20
  3153.         _.yoffset = 1
  3154.  
  3155.     
  3156.     def setText(_, text):
  3157.         _.text = text
  3158.  
  3159.     
  3160.     def _unbind(_):
  3161.         if _.bindings and _.widget:
  3162.             _.widget.unbind('<Enter>', _.bindings[0])
  3163.             _.widget.unbind('<Leave>', _.bindings[1])
  3164.             _.widget.unbind('<ButtonPress>', _.bindings[2])
  3165.             _.bindings = []
  3166.         
  3167.  
  3168.     
  3169.     def destroy(_):
  3170.         _._unbind()
  3171.         _._leave()
  3172.  
  3173.     
  3174.     def _enter(_, *event):
  3175.         after_cancel(_.timer)
  3176.         _.timer = after(_.widget, _.time, _._showTip)
  3177.  
  3178.     
  3179.     def _leave(_, *event):
  3180.         after_cancel(_.timer)
  3181.         _.timer = None
  3182.         if _.tooltip:
  3183.             _.label.destroy()
  3184.             destruct(_.label)
  3185.             _.label = None
  3186.             _.tooltip.destroy()
  3187.             destruct(_.tooltip)
  3188.             _.tooltip = None
  3189.         
  3190.  
  3191.     
  3192.     def _showTip(_):
  3193.         if _.tooltip or not (_.text):
  3194.             return None
  3195.         
  3196.         c = _.widget.__class__
  3197.         if c in (Tkinter.Button,):
  3198.             if _.widget['state'] == Tkinter.DISABLED:
  3199.                 return None
  3200.             
  3201.         
  3202.         x = _.widget.winfo_rootx()
  3203.         y = _.widget.winfo_rooty() + _.widget.winfo_height()
  3204.         x = x + _.xoffset
  3205.         y = y + _.yoffset
  3206.         _.tooltip = Tkinter.Toplevel()
  3207.         _.tooltip.wm_iconify()
  3208.         _.tooltip.wm_overrideredirect(1)
  3209.         _.tooltip.wm_protocol('WM_DELETE_WINDOW', _.destroy)
  3210.         _.label = Tkinter.Label(_.tooltip, text = _.text, relief = _.relief, justify = _.justify, fg = _.fg, bg = _.bg, bd = 1, takefocus = 0)
  3211.         _.label.pack(ipadx = 1, ipady = 1)
  3212.         if os.name == 'nt':
  3213.             pass
  3214.         need_fix = tkversion < (8, 3, 0, 0)
  3215.         if not need_fix:
  3216.             _.tooltip.wm_geometry('%+d%+d' % (x, y))
  3217.         
  3218.         _.tooltip.wm_deiconify()
  3219.         if need_fix:
  3220.             _.tooltip.wm_geometry('%+d%+d' % (x, y))
  3221.         
  3222.  
  3223.  
  3224.  
  3225. class MfxScrolledCanvas:
  3226.     
  3227.     def __init__(_, parent, vbar = 1, hbar = 0, **kw):
  3228.         bg = kw.get('bg', parent.cget('bg'))
  3229.         kwdefault(kw, bg = bg, highlightthickness = 0, xscrollincrement = 16, yscrollincrement = 16)
  3230.         _.parent = parent
  3231.         _.frame = Tkinter.Frame(parent, bg = bg)
  3232.         _.frame.grid_rowconfigure(0, weight = 1)
  3233.         _.frame.grid_columnconfigure(0, weight = 1)
  3234.         _.canvas = None
  3235.         _.vbar = None
  3236.         _.vbar_mode = vbar
  3237.         _.hbar = None
  3238.         _.hbar_mode = hbar
  3239.         _.createCanvas(kw)
  3240.         if vbar:
  3241.             _.createVbar(bg)
  3242.         
  3243.         if hbar:
  3244.             _.createHbar(bg)
  3245.         
  3246.         _.canvas.focus_set()
  3247.  
  3248.     
  3249.     def unbind(_):
  3250.         unbind_destroy(_.canvas)
  3251.         unbind_destroy(_.frame)
  3252.  
  3253.     
  3254.     def destroy(_):
  3255.         _.unbind()
  3256.  
  3257.     
  3258.     def createCanvas(_, kw):
  3259.         _.canvas = apply(Tkinter.Canvas, (_.frame,), kw)
  3260.         _.canvas.grid(row = 0, column = 0, sticky = 'nsew')
  3261.  
  3262.     
  3263.     def createVbar(_, bg):
  3264.         _.vbar = Tkinter.Scrollbar(_.frame, name = 'vbar', bg = bg, takefocus = 0)
  3265.         _.canvas['yscrollcommand'] = _.vbar.set
  3266.         _.vbar['command'] = _.canvas.yview
  3267.         w = _.canvas
  3268.         bind(w, '<KeyPress-Prior>', _.page_up)
  3269.         bind(w, '<KeyPress-Next>', _.page_down)
  3270.         bind(w, '<KeyPress-Up>', _.unit_up)
  3271.         bind(w, '<KeyPress-Down>', _.unit_down)
  3272.         bind(w, '<KeyPress-Begin>', _.scroll_top)
  3273.         bind(w, '<KeyPress-Home>', _.scroll_top)
  3274.         bind(w, '<KeyPress-End>', _.scroll_bottom)
  3275.  
  3276.     
  3277.     def createHbar(_, bg):
  3278.         _.hbar = Tkinter.Scrollbar(_.frame, name = 'hbar', bg = bg, takefocus = 0, orient = 'horizontal')
  3279.         _.canvas['xscrollcommand'] = _.hbar.set
  3280.         _.hbar['command'] = _.canvas.xview
  3281.         w = _.canvas
  3282.         bind(w, '<KeyPress-Left>', _.unit_left)
  3283.         bind(w, '<KeyPress-Right>', _.unit_right)
  3284.  
  3285.     
  3286.     def showVbar(_, show = -1):
  3287.         if not (_.vbar):
  3288.             return None
  3289.         
  3290.         if show < 0:
  3291.             show = _.vbar_mode
  3292.         
  3293.         if show > 1:
  3294.             view = _.canvas.yview()
  3295.             if not abs(view[0]) > 0.0001:
  3296.                 pass
  3297.             show = abs(view[1] - 1.0) > 0.0001
  3298.         
  3299.         if show:
  3300.             _.vbar.grid(row = 0, column = 1, sticky = 'nse')
  3301.             _.frame.grid_columnconfigure(1, minsize = 0)
  3302.         else:
  3303.             w = 21
  3304.             b = _.frame.grid_bbox(row = 0, column = 2)
  3305.             if b and len(b) == 4 and b[2] > 0 and b[3] > 0:
  3306.                 w = b[2]
  3307.             
  3308.             _.vbar.grid_forget()
  3309.             _.frame.grid_columnconfigure(1, minsize = w)
  3310.  
  3311.     
  3312.     def showHbar(_, show = -1):
  3313.         if not (_.hbar):
  3314.             return None
  3315.         
  3316.         if show < 0:
  3317.             show = _.hbar_mode
  3318.         
  3319.         if show > 1:
  3320.             view = _.canvas.xview()
  3321.             if not abs(view[0]) > 0.0001:
  3322.                 pass
  3323.             show = abs(view[1] - 1.0) > 0.0001
  3324.         
  3325.         if show:
  3326.             _.hbar.grid(row = 1, column = 0, sticky = 'ews')
  3327.             _.frame.grid_rowconfigure(1, minsize = 0)
  3328.         else:
  3329.             w = 21
  3330.             b = _.frame.grid_bbox(row = 2, column = 0)
  3331.             if b and len(b) == 4 and b[2] > 0 and b[3] > 0:
  3332.                 w = b[3]
  3333.             
  3334.             _.hbar.grid_forget()
  3335.             _.frame.grid_rowconfigure(1, minsize = w)
  3336.  
  3337.     
  3338.     def page_up(_, *event):
  3339.         _.canvas.yview_scroll(-1, 'page')
  3340.         return 'break'
  3341.  
  3342.     
  3343.     def page_down(_, *event):
  3344.         _.canvas.yview_scroll(1, 'page')
  3345.         return 'break'
  3346.  
  3347.     
  3348.     def unit_up(_, *event):
  3349.         _.canvas.yview_scroll(-1, 'unit')
  3350.         return 'break'
  3351.  
  3352.     
  3353.     def unit_down(_, *event):
  3354.         _.canvas.yview_scroll(1, 'unit')
  3355.         return 'break'
  3356.  
  3357.     
  3358.     def page_left(_, *event):
  3359.         _.canvas.xview_scroll(-1, 'page')
  3360.         return 'break'
  3361.  
  3362.     
  3363.     def page_right(_, *event):
  3364.         _.canvas.xview_scroll(1, 'page')
  3365.         return 'break'
  3366.  
  3367.     
  3368.     def unit_left(_, *event):
  3369.         _.canvas.xview_scroll(-1, 'unit')
  3370.         return 'break'
  3371.  
  3372.     
  3373.     def unit_right(_, *event):
  3374.         _.canvas.xview_scroll(1, 'unit')
  3375.         return 'break'
  3376.  
  3377.     
  3378.     def scroll_top(_, *event):
  3379.         _.canvas.yview_moveto(0)
  3380.         return 'break'
  3381.  
  3382.     
  3383.     def scroll_bottom(_, *event):
  3384.         _.canvas.yview_moveto(1)
  3385.         return 'break'
  3386.  
  3387.  
  3388.  
  3389. class MfxScrolledText(Tkinter.Text):
  3390.     
  3391.     def __init__(_, parent = None, **cnf):
  3392.         fcnf = { }
  3393.         for k in cnf.keys():
  3394.             pass
  3395.         
  3396.         if cnf.has_key('bg'):
  3397.             fcnf['bg'] = cnf['bg']
  3398.         
  3399.         _.frame = apply(Tkinter.Frame, (parent,), fcnf)
  3400.         _.vbar = Tkinter.Scrollbar(_.frame, name = 'vbar')
  3401.         _.vbar.pack(side = Tkinter.RIGHT, fill = Tkinter.Y)
  3402.         cnf['name'] = 'text'
  3403.         apply(Tkinter.Text.__init__, (_, _.frame), cnf)
  3404.         _.pack(side = Tkinter.LEFT, fill = Tkinter.BOTH, expand = 1)
  3405.         _['yscrollcommand'] = _.vbar.set
  3406.         _.vbar['command'] = _.yview
  3407.         for m in Tkinter.Pack.__dict__.keys():
  3408.             pass
  3409.         
  3410.         _.frame['highlightthickness'] = 0
  3411.         _.vbar['highlightthickness'] = 0
  3412.  
  3413.     
  3414.     def xview_moveto(_, fraction):
  3415.         return _.tk.call(_._w, 'xview', 'moveto', fraction)
  3416.  
  3417.     
  3418.     def xview_scroll(_, number, what):
  3419.         return _.tk.call(_._w, 'xview', 'scroll', number, what)
  3420.  
  3421.     
  3422.     def yview_moveto(_, fraction):
  3423.         return _.tk.call(_._w, 'yview', 'moveto', fraction)
  3424.  
  3425.     
  3426.     def yview_scroll(_, number, what):
  3427.         return _.tk.call(_._w, 'yview', 'scroll', number, what)
  3428.  
  3429.  
  3430.  
  3431. class MfxReadonlyScrolledText(MfxScrolledText):
  3432.     
  3433.     def __init__(_, parent = None, **cnf):
  3434.         apply(MfxScrolledText.__init__, (_, parent), cnf)
  3435.         _.config(state = 'disabled', insertofftime = 0)
  3436.         _.frame.config(takefocus = 0)
  3437.         _.config(takefocus = 0)
  3438.         _.vbar.config(takefocus = 0)
  3439.  
  3440.  
  3441.  
  3442. class tkHTMLWriter(formatter.DumbWriter):
  3443.     
  3444.     def __init__(_, text, viewer):
  3445.         formatter.DumbWriter.__init__(_, _, maxcol = 9999)
  3446.         _.text = text
  3447.         _.viewer = viewer
  3448.         (font, size) = ('Helvetica', 12)
  3449.         f = _.text['font']
  3450.         if f[0] == '{':
  3451.             m = re.search('^\\{([^\\}]+)\\}\\s*(-?\\d+)', f)
  3452.             if m:
  3453.                 (font, size) = (m.group(1), int(m.group(2)))
  3454.             
  3455.         else:
  3456.             f = string.split(f)
  3457.             (font, size) = (f[0], int(f[1]))
  3458.         sign = 1
  3459.         if size < 0:
  3460.             sign = -1
  3461.         
  3462.         fixed = ('Courier', 12)
  3463.         if os.name == 'nt':
  3464.             fixed = ('Courier New', 10)
  3465.         
  3466.         _.fontmap = {
  3467.             'h1': (font, size + 12 * sign, 'bold'),
  3468.             'h2': (font, size + 8 * sign, 'bold'),
  3469.             'h3': (font, size + 6 * sign, 'bold'),
  3470.             'h4': (font, size + 4 * sign, 'bold'),
  3471.             'h5': (font, size + 2 * sign, 'bold'),
  3472.             'h6': (font, size + 1 * sign, 'bold'),
  3473.             'bold': (font, size, 'bold'),
  3474.             'italic': (font, size, 'italic'),
  3475.             'pre': fixed }
  3476.         _.text.config(cursor = _.viewer.defcursor)
  3477.         for f in _.fontmap.keys():
  3478.             _.text.tag_config(f, font = _.fontmap[f])
  3479.         
  3480.         _.anchor = None
  3481.         _.anchor_mark = None
  3482.         _.font = None
  3483.         _.font_mark = None
  3484.         _.indent = ''
  3485.  
  3486.     
  3487.     def createCallback(_, href):
  3488.         
  3489.         class Functor:
  3490.             
  3491.             def __init__(_, viewer, arg):
  3492.                 _.viewer = viewer
  3493.                 _.arg = arg
  3494.  
  3495.             
  3496.             def __call__(_, *args):
  3497.                 _.viewer.updateHistoryXYView()
  3498.                 return _.viewer.display(_.arg)
  3499.  
  3500.  
  3501.         return Functor(_.viewer, href)
  3502.  
  3503.     
  3504.     def write(_, data):
  3505.         _.text.insert('insert', data)
  3506.  
  3507.     
  3508.     def __write(_, data):
  3509.         _.text.insert('insert', data)
  3510.  
  3511.     
  3512.     def anchor_bgn(_, href, name, type):
  3513.         if href:
  3514.             _.anchor = (href, name, type)
  3515.             _.anchor_mark = _.text.index('insert')
  3516.         
  3517.  
  3518.     
  3519.     def anchor_end(_):
  3520.         if _.anchor:
  3521.             url = _.anchor[0]
  3522.             tag = 'href_' + url
  3523.             _.text.tag_add(tag, _.anchor_mark, 'insert')
  3524.             _.text.tag_bind(tag, '<ButtonPress>', _.createCallback(url))
  3525.             _.text.tag_bind(tag, '<Enter>', _.anchor_enter)
  3526.             _.text.tag_bind(tag, '<Leave>', _.anchor_leave)
  3527.             _.text.tag_config(tag, foreground = 'blue', underline = 1)
  3528.             _.anchor = None
  3529.         
  3530.  
  3531.     
  3532.     def anchor_enter(_, *args):
  3533.         _.text.config(cursor = _.viewer.handcursor)
  3534.  
  3535.     
  3536.     def anchor_leave(_, *args):
  3537.         _.text.config(cursor = _.viewer.defcursor)
  3538.  
  3539.     
  3540.     def new_font(_, font):
  3541.         if _.font:
  3542.             _.text.tag_add(_.font, _.font_mark, 'insert')
  3543.             _.font = None
  3544.         
  3545.         if font:
  3546.             _.font_mark = _.text.index('insert')
  3547.             if _.fontmap.has_key(font[0]):
  3548.                 _.font = font[0]
  3549.             elif font[3]:
  3550.                 _.font = 'pre'
  3551.             elif font[2]:
  3552.                 _.font = 'bold'
  3553.             elif font[1]:
  3554.                 _.font = 'italic'
  3555.             else:
  3556.                 _.font = None
  3557.         
  3558.  
  3559.     
  3560.     def new_margin(_, margin, level):
  3561.         _.indent = '    ' * level
  3562.  
  3563.     
  3564.     def send_label_data(_, data):
  3565.         _._tkHTMLWriter__write(_.indent + data + ' ')
  3566.  
  3567.     
  3568.     def send_paragraph(_, blankline):
  3569.         if _.col > 0:
  3570.             _._tkHTMLWriter__write('\n')
  3571.         
  3572.         if blankline > 0:
  3573.             _._tkHTMLWriter__write('\n' * blankline)
  3574.         
  3575.         _.col = 0
  3576.         _.atbreak = 0
  3577.  
  3578.     
  3579.     def send_hor_rule(_, *args):
  3580.         width = int(int(_.text['width']) * 0.9)
  3581.         _._tkHTMLWriter__write('_' * width)
  3582.         _._tkHTMLWriter__write('\n')
  3583.         _.col = 0
  3584.         _.atbreak = 0
  3585.  
  3586.  
  3587.  
  3588. class tkHTMLParser(htmllib.HTMLParser):
  3589.     
  3590.     def anchor_bgn(_, href, name, type):
  3591.         htmllib.HTMLParser.anchor_bgn(_, href, name, type)
  3592.         _.formatter.writer.anchor_bgn(href, name, type)
  3593.  
  3594.     
  3595.     def anchor_end(_):
  3596.         if _.anchor:
  3597.             _.anchor = None
  3598.         
  3599.         _.formatter.writer.anchor_end()
  3600.  
  3601.     
  3602.     def do_dt(_, attrs):
  3603.         _.formatter.end_paragraph(1)
  3604.         _.ddpop()
  3605.  
  3606.     
  3607.     def handle_image(_, src, alt, ismap, align, width, height):
  3608.         _.formatter.writer.viewer.showImage(src, alt, ismap, align, width, height)
  3609.  
  3610.  
  3611.  
  3612. class tkHTMLViewer:
  3613.     
  3614.     def __init__(_, parent):
  3615.         _.parent = parent
  3616.         _.home = None
  3617.         _.url = None
  3618.         _.history = Struct(list = [], index = 0)
  3619.         _.images = []
  3620.         _.defcursor = parent['cursor']
  3621.         _.handcursor = 'hand2'
  3622.         frame = _.frame = Tkinter.Frame(parent)
  3623.         frame.pack(side = 'bottom', fill = 'x')
  3624.         _.homeButton = Tkinter.Button(frame, text = 'Index', command = _.goHome)
  3625.         _.homeButton.pack(side = 'left')
  3626.         _.backButton = Tkinter.Button(frame, text = 'Back', command = _.goBack)
  3627.         _.backButton.pack(side = 'left')
  3628.         _.forwardButton = Tkinter.Button(frame, text = 'Forward', command = _.goForward)
  3629.         _.forwardButton.pack(side = 'left')
  3630.         _.closeButton = Tkinter.Button(frame, text = 'Close', command = _.destroy)
  3631.         _.closeButton.pack(side = 'right')
  3632.         basefont = ('Helvetica', 12)
  3633.         if os.name == 'nt':
  3634.             basefont = ('Times New Roman', 12)
  3635.         
  3636.         _.text = MfxReadonlyScrolledText(parent, fg = '#000000', bg = '#f7f3ff', cursor = _.defcursor, font = basefont, wrap = 'word', padx = 20, pady = 20)
  3637.         _.text.pack(side = 'top', fill = 'both', expand = 1)
  3638.         _.initBindings()
  3639.  
  3640.     
  3641.     def initBindings(_):
  3642.         w = _.parent
  3643.         bind(w, 'WM_DELETE_WINDOW', _.destroy)
  3644.         bind(w, '<Escape>', _.destroy)
  3645.         bind(w, '<KeyPress-Prior>', _.page_up)
  3646.         bind(w, '<KeyPress-Next>', _.page_down)
  3647.         bind(w, '<KeyPress-Up>', _.unit_up)
  3648.         bind(w, '<KeyPress-Down>', _.unit_down)
  3649.         bind(w, '<KeyPress-Begin>', _.scroll_top)
  3650.         bind(w, '<KeyPress-Home>', _.scroll_top)
  3651.         bind(w, '<KeyPress-End>', _.scroll_bottom)
  3652.         bind(w, '<KeyPress-BackSpace>', _.goBack)
  3653.  
  3654.     
  3655.     def destroy(_, *event):
  3656.         unbind_destroy(_.parent)
  3657.         
  3658.         try:
  3659.             _.parent.wm_withdraw()
  3660.         except:
  3661.             pass
  3662.  
  3663.         
  3664.         try:
  3665.             _.parent.destroy()
  3666.         except:
  3667.             pass
  3668.  
  3669.         _.parent = None
  3670.  
  3671.     
  3672.     def page_up(_, *event):
  3673.         _.text.yview_scroll(-1, 'page')
  3674.         return 'break'
  3675.  
  3676.     
  3677.     def page_down(_, *event):
  3678.         _.text.yview_scroll(1, 'page')
  3679.         return 'break'
  3680.  
  3681.     
  3682.     def unit_up(_, *event):
  3683.         _.text.yview_scroll(-1, 'unit')
  3684.         return 'break'
  3685.  
  3686.     
  3687.     def unit_down(_, *event):
  3688.         _.text.yview_scroll(1, 'unit')
  3689.         return 'break'
  3690.  
  3691.     
  3692.     def scroll_top(_, *event):
  3693.         _.text.yview_moveto(0)
  3694.         return 'break'
  3695.  
  3696.     
  3697.     def scroll_bottom(_, *event):
  3698.         _.text.yview_moveto(1)
  3699.         return 'break'
  3700.  
  3701.     
  3702.     def basejoin(_, url, baseurl = None, relpath = 1):
  3703.         if baseurl is None:
  3704.             baseurl = _.url
  3705.         
  3706.         url = os.path.normpath(url)
  3707.         if relpath and baseurl and not os.path.isabs(url):
  3708.             (h1, t1) = os.path.split(url)
  3709.             (h2, t2) = os.path.split(baseurl)
  3710.             if cmp(h1, h2) != 0:
  3711.                 url = os.path.join(h2, h1, t1)
  3712.             
  3713.             url = os.path.normpath(url)
  3714.         
  3715.         return url
  3716.  
  3717.     
  3718.     def display(_, url, add = 1, relpath = 1, xview = 0, yview = 0):
  3719.         if _.__dict__.get('app'):
  3720.             if _.app and _.app.game:
  3721.                 _.app.game._cancelDrag()
  3722.             
  3723.         
  3724.         for p in ('ftp:', 'gopher:', 'http:', 'mailto:', 'news:', 'telnet:'):
  3725.             if string.find(url, p) != -1:
  3726.                 return None
  3727.             
  3728.         
  3729.         url = _.basejoin(url, relpath = relpath)
  3730.         
  3731.         try:
  3732.             file = None
  3733.             if os.path.isdir(url):
  3734.                 url = os.path.join(url, 'index.html')
  3735.             
  3736.             url = os.path.normpath(url)
  3737.             file = open(url)
  3738.             data = file.read()
  3739.             file.close()
  3740.             file = None
  3741.         except Exception:
  3742.             ex = None
  3743.             if file:
  3744.                 file.close()
  3745.             
  3746.             _.errorDialog('Unable to service request:\n' + url + '\n\n' + str(ex))
  3747.             return None
  3748.         except:
  3749.             if file:
  3750.                 file.close()
  3751.             
  3752.             _.errorDialog('Unable to service request:\n' + url)
  3753.             return None
  3754.  
  3755.         _.url = url
  3756.         if _.home is None:
  3757.             _.home = _.url
  3758.         
  3759.         if add:
  3760.             _.addHistory(_.url, xview = xview, yview = yview)
  3761.         
  3762.         if _.history.index > 1:
  3763.             _.backButton.config(state = 'normal')
  3764.         else:
  3765.             _.backButton.config(state = 'disabled')
  3766.         if _.history.index < len(_.history.list):
  3767.             _.forwardButton.config(state = 'normal')
  3768.         else:
  3769.             _.forwardButton.config(state = 'disabled')
  3770.         (old_c1, old_c2) = (_.defcursor, _.handcursor)
  3771.         _.defcursor = _.handcursor = 'watch'
  3772.         _.text.config(cursor = _.defcursor)
  3773.         _.text.update_idletasks()
  3774.         _.frame.config(cursor = _.defcursor)
  3775.         _.frame.update_idletasks()
  3776.         _.text.config(state = 'normal')
  3777.         _.text.delete('1.0', 'end')
  3778.         _.images = []
  3779.         writer = tkHTMLWriter(_.text, _)
  3780.         fmt = formatter.AbstractFormatter(writer)
  3781.         parser = tkHTMLParser(fmt)
  3782.         parser.feed(data)
  3783.         parser.close()
  3784.         _.text.config(state = 'disabled')
  3785.         if xview <= xview:
  3786.             pass
  3787.         elif xview <= 1.0:
  3788.             _.text.xview_moveto(xview)
  3789.         
  3790.         if yview <= yview:
  3791.             pass
  3792.         elif yview <= 1.0:
  3793.             _.text.yview_moveto(yview)
  3794.         
  3795.         _.parent.wm_title(parser.title)
  3796.         _.parent.wm_iconname(parser.title)
  3797.         (_.defcursor, _.handcursor) = (old_c1, old_c2)
  3798.         _.text.config(cursor = _.defcursor)
  3799.         _.frame.config(cursor = _.defcursor)
  3800.  
  3801.     
  3802.     def addHistory(_, url, xview = 0, yview = 0):
  3803.         if _.history.index > 0:
  3804.             (u, xv, yv) = _.history.list[_.history.index - 1]
  3805.             if cmp(u, url) == 0:
  3806.                 _.updateHistoryXYView()
  3807.                 return None
  3808.             
  3809.         
  3810.         del _.history.list[_.history.index:]
  3811.         _.history.list.append((url, xview, yview))
  3812.         _.history.index = _.history.index + 1
  3813.  
  3814.     
  3815.     def updateHistoryXYView(_):
  3816.         if _.history.index > 0:
  3817.             (url, xview, yview) = _.history.list[_.history.index - 1]
  3818.             xview = _.text.xview()[0]
  3819.             yview = _.text.yview()[0]
  3820.             _.history.list[_.history.index - 1] = (url, xview, yview)
  3821.         
  3822.  
  3823.     
  3824.     def goBack(_, *event):
  3825.         if _.history.index > 1:
  3826.             _.updateHistoryXYView()
  3827.             _.history.index = _.history.index - 1
  3828.             (url, xview, yview) = _.history.list[_.history.index - 1]
  3829.             _.display(url, add = 0, relpath = 0, xview = xview, yview = yview)
  3830.         
  3831.  
  3832.     
  3833.     def goForward(_, *event):
  3834.         if _.history.index < len(_.history.list):
  3835.             _.updateHistoryXYView()
  3836.             (url, xview, yview) = _.history.list[_.history.index]
  3837.             _.history.index = _.history.index + 1
  3838.             _.display(url, add = 0, relpath = 0, xview = xview, yview = yview)
  3839.         
  3840.  
  3841.     
  3842.     def goHome(_, *event):
  3843.         if _.home and cmp(_.home, _.url) != 0:
  3844.             _.updateHistoryXYView()
  3845.             _.display(_.home, relpath = 0)
  3846.         
  3847.  
  3848.     
  3849.     def errorDialog(_, msg):
  3850.         d = MfxDialog(_.parent, title = PACKAGE + ' HTML Problem', text = msg, bitmap = 'warning', strings = ('OK',), default = 0)
  3851.  
  3852.     
  3853.     def showImage(_, src, alt, ismap, align, width, height):
  3854.         url = _.basejoin(src)
  3855.         
  3856.         try:
  3857.             img = Tkinter.PhotoImage(file = url)
  3858.         except Tkinter.TclError:
  3859.             img = None
  3860.         except:
  3861.             img = None
  3862.  
  3863.         if img:
  3864.             (padx, pady) = (10, 10)
  3865.             (padx, pady) = (0, 20)
  3866.             if string.lower(align) == 'left':
  3867.                 padx = 0
  3868.             
  3869.             _.text.image_create(index = 'insert', image = img, padx = padx, pady = pady)
  3870.             _.images.append(img)
  3871.         
  3872.  
  3873.  
  3874.  
  3875. class DisplayTextDialog(MfxDialog):
  3876.     Text_Class = MfxReadonlyScrolledText
  3877.     
  3878.     def __init__(_, parent, title, text, **kw):
  3879.         kw = _.initKw(kw)
  3880.         _ToplevelDialog.__init__(_, parent, title, kw.resizable, kw.default)
  3881.         (top_frame, bottom_frame) = _.createFrames(kw)
  3882.         _.createBitmaps(top_frame, kw)
  3883.         bg = top_frame['bg']
  3884.         _.text_w = _.Text_Class(top_frame, bd = 1, relief = 'sunken', wrap = 'word', width = 80, height = 30, bg = bg)
  3885.         _.text = ''
  3886.         if text:
  3887.             _.text = str(text)
  3888.             old_state = _.text_w['state']
  3889.             _.text_w.config(state = 'normal')
  3890.             _.text_w.insert('insert', _.text)
  3891.             _.text_w.config(state = old_state)
  3892.         
  3893.         _.text_w.pack(side = 'top', fill = 'both', expand = 1)
  3894.         focus = _.createButtons(bottom_frame, kw)
  3895.         focus = _.text_w
  3896.         _.mainloop(focus, kw.timeout)
  3897.  
  3898.     
  3899.     def initKw(_, kw):
  3900.         kw = KwStruct(kw, strings = ('OK',), default = 0, resizable = 1, separatorwidth = 0)
  3901.         return MfxDialog.initKw(_, kw)
  3902.  
  3903.  
  3904.  
  3905. class EditTextDialog(DisplayTextDialog):
  3906.     Text_Class = MfxScrolledText
  3907.     
  3908.     def initKw(_, kw):
  3909.         kw = KwStruct(kw, strings = ('OK', 'Cancel'), default = -1, resizable = 1, separatorwidth = 0)
  3910.         return MfxDialog.initKw(_, kw)
  3911.  
  3912.     
  3913.     def destroy(_):
  3914.         _.text = _.text_w.get('1.0', 'end')
  3915.         DisplayTextDialog.destroy(_)
  3916.  
  3917.     
  3918.     def wmDeleteWindow(_, *event):
  3919.         pass
  3920.  
  3921.     
  3922.     def mCancel(_, *event):
  3923.         pass
  3924.  
  3925.  
  3926.  
  3927. class PlayerOptionsDialog(MfxDialog):
  3928.     
  3929.     def __init__(_, parent, title, app, **kw):
  3930.         kw = _.initKw(kw)
  3931.         _ToplevelDialog.__init__(_, parent, title, kw.resizable, kw.default)
  3932.         (top_frame, bottom_frame) = _.createFrames(kw)
  3933.         _.createBitmaps(top_frame, kw)
  3934.         _.update_stats_var = Tkinter.BooleanVar()
  3935.         _.update_stats_var.set(app.opt.update_player_stats != 0)
  3936.         _.confirm_var = Tkinter.BooleanVar()
  3937.         _.confirm_var.set(app.opt.confirm != 0)
  3938.         _.win_animation_var = Tkinter.BooleanVar()
  3939.         _.win_animation_var.set(app.opt.win_animation != 0)
  3940.         widget = Tkinter.Label(top_frame, text = '\nPlease enter your name', takefocus = 0)
  3941.         widget.pack(pady = 5)
  3942.         w = kw.get('e_width', 30)
  3943.         _.player_var = Tkinter.Entry(top_frame, exportselection = 1, width = w)
  3944.         _.player_var.insert(0, app.opt.player)
  3945.         _.player_var.pack(side = Tkinter.TOP, padx = kw.padx, pady = kw.pady)
  3946.         widget = Tkinter.Checkbutton(top_frame, variable = _.confirm_var, text = 'Confirm quit')
  3947.         widget.pack(side = Tkinter.TOP, padx = kw.padx, pady = kw.pady)
  3948.         widget = Tkinter.Checkbutton(top_frame, variable = _.update_stats_var, text = 'Update statistics and logs')
  3949.         widget.pack(side = Tkinter.TOP, padx = kw.padx, pady = kw.pady)
  3950.         _.player = _.player_var.get()
  3951.         _.confirm = _.confirm_var.get()
  3952.         _.update_stats = _.update_stats_var.get()
  3953.         _.win_animation = _.win_animation_var.get()
  3954.         focus = _.createButtons(bottom_frame, kw)
  3955.         _.mainloop(focus, kw.timeout)
  3956.  
  3957.     
  3958.     def mDone(_, button):
  3959.         _.button = button
  3960.         _.player = _.player_var.get()
  3961.         _.confirm = _.confirm_var.get()
  3962.         _.update_stats = _.update_stats_var.get()
  3963.         _.win_animation = _.win_animation_var.get()
  3964.         raise SystemExit
  3965.  
  3966.     
  3967.     def initKw(_, kw):
  3968.         kw = KwStruct(kw, strings = ('OK', 'Cancel'), default = 0, separatorwidth = 2, resizable = 1, padx = 10, pady = 10)
  3969.         return MfxDialog.initKw(_, kw)
  3970.  
  3971.  
  3972.  
  3973. class DemoOptionsDialog(MfxDialog):
  3974.     
  3975.     def __init__(_, parent, title, app, **kw):
  3976.         kw = _.initKw(kw)
  3977.         _ToplevelDialog.__init__(_, parent, title, kw.resizable, kw.default)
  3978.         (top_frame, bottom_frame) = _.createFrames(kw)
  3979.         _.createBitmaps(top_frame, kw)
  3980.         _.demo_logo_var = Tkinter.BooleanVar()
  3981.         _.demo_logo_var.set(app.opt.demo_logo != 0)
  3982.         _.demo_score_var = Tkinter.BooleanVar()
  3983.         _.demo_score_var.set(app.opt.demo_score != 0)
  3984.         _.demo_sleep_var = Tkinter.DoubleVar()
  3985.         _.demo_sleep_var.set(app.opt.demo_sleep)
  3986.         widget = Tkinter.Checkbutton(top_frame, variable = _.demo_logo_var, text = 'Display floating Demo logo')
  3987.         widget.pack(side = Tkinter.TOP, padx = kw.padx, pady = kw.pady)
  3988.         widget = Tkinter.Checkbutton(top_frame, variable = _.demo_score_var, text = 'Show score in statusbar')
  3989.         widget.pack(side = Tkinter.TOP, padx = kw.padx, pady = kw.pady)
  3990.         widget = Tkinter.Scale(top_frame, from_ = 0.2, to = 9.9, resolution = 0.1, orient = Tkinter.HORIZONTAL, length = '3i', label = 'Set demo delay in seconds', variable = _.demo_sleep_var, takefocus = 0)
  3991.         widget.pack(side = Tkinter.TOP, padx = kw.padx, pady = kw.pady)
  3992.         focus = _.createButtons(bottom_frame, kw)
  3993.         _.mainloop(focus, kw.timeout)
  3994.         _.demo_logo = _.demo_logo_var.get()
  3995.         _.demo_score = _.demo_score_var.get()
  3996.         _.demo_sleep = _.demo_sleep_var.get()
  3997.  
  3998.     
  3999.     def initKw(_, kw):
  4000.         kw = KwStruct(kw, strings = ('OK', 'Cancel'), default = 0, separatorwidth = 0)
  4001.         return MfxDialog.initKw(_, kw)
  4002.  
  4003.  
  4004.  
  4005. class HintOptionsDialog:
  4006.     
  4007.     def __init__(_, parent, title, app, **kw):
  4008.         d = MfxSimpleSlider(parent, title, 'Set hint delay in seconds', app.opt.hint_sleep, 0.2, 9.9, 0.1)
  4009.         _.status = d.status
  4010.         _.button = d.button
  4011.         _.hint_sleep = d.value
  4012.  
  4013.  
  4014.  
  4015. class SoundOptionsDialog(MfxDialog):
  4016.     MIXER = ()
  4017.     
  4018.     def __init__(_, parent, title, app, **kw):
  4019.         _.app = app
  4020.         kw = _.initKw(kw)
  4021.         _ToplevelDialog.__init__(_, parent, title, kw.resizable, kw.default)
  4022.         (top_frame, bottom_frame) = _.createFrames(kw)
  4023.         _.createBitmaps(top_frame, kw)
  4024.         _.saved_opt = app.opt.copy()
  4025.         _.sound = Tkinter.BooleanVar()
  4026.         _.sound.set(app.opt.sound != 0)
  4027.         _.sound_mode = Tkinter.BooleanVar()
  4028.         _.sound_mode.set(app.opt.sound_mode != 0)
  4029.         _.sample_volume = Tkinter.IntVar()
  4030.         _.sample_volume.set(app.opt.sound_sample_volume)
  4031.         _.music_volume = Tkinter.IntVar()
  4032.         _.music_volume.set(app.opt.sound_music_volume)
  4033.         widget = Tkinter.Checkbutton(top_frame, variable = _.sound, text = 'Sound enabled')
  4034.         widget.pack(side = Tkinter.TOP, padx = kw.padx, pady = kw.pady)
  4035.         if os.name == 'nt' and pysolsoundserver:
  4036.             widget = Tkinter.Checkbutton(top_frame, variable = _.sound_mode, text = 'Use DirectX for sound playing', command = _.mOptSoundDirectX)
  4037.             widget.pack(side = Tkinter.TOP, padx = kw.padx, pady = kw.pady)
  4038.         
  4039.         if pysolsoundserver and app.startup_opt.sound_mode > 0:
  4040.             widget = Tkinter.Scale(top_frame, from_ = 0, to = 128, resolution = 1, orient = Tkinter.HORIZONTAL, length = '3i', label = 'Sample volume', variable = _.sample_volume, takefocus = 0)
  4041.             widget.pack(side = Tkinter.TOP, padx = kw.padx, pady = kw.pady)
  4042.             widget = Tkinter.Scale(top_frame, from_ = 0, to = 128, resolution = 1, orient = Tkinter.HORIZONTAL, length = '3i', label = 'Music volume', variable = _.music_volume, takefocus = 0)
  4043.             widget.pack(side = Tkinter.TOP, padx = kw.padx, pady = kw.pady)
  4044.         else:
  4045.             kw.strings[1] = None
  4046.         focus = _.createButtons(bottom_frame, kw)
  4047.         _.mainloop(focus, kw.timeout)
  4048.  
  4049.     
  4050.     def initKw(_, kw):
  4051.         strings = [
  4052.             'OK',
  4053.             'Apply',
  4054.             'Mixer...',
  4055.             'Cancel']
  4056.         if _.MIXER is None:
  4057.             strings[2] = ('Mixer...', -1)
  4058.         
  4059.         kw = KwStruct(kw, strings = strings, default = 0, separatorwidth = 2, resizable = 1, font = None, padx = 10, pady = 10, buttonpadx = 10, buttonpady = 5)
  4060.         return MfxDialog.initKw(_, kw)
  4061.  
  4062.     
  4063.     def mDone(_, button):
  4064.         if button == 0 or button == 1:
  4065.             _.app.opt.sound = _.sound.get()
  4066.             _.app.opt.sound_mode = _.sound_mode.get()
  4067.             _.app.opt.sound_sample_volume = _.sample_volume.get()
  4068.             _.app.opt.sound_music_volume = _.music_volume.get()
  4069.         elif button == 2:
  4070.             MIXERS = ()
  4071.             if os.name == 'nt':
  4072.                 MIXERS = (('sndvol32.exe', None),)
  4073.             elif os.name == 'posix':
  4074.                 MIXERS = (('kmix', None), ('gmix', None))
  4075.             
  4076.             for name, args in MIXERS:
  4077.                 
  4078.                 try:
  4079.                     f = spawnvp(name, args)
  4080.                     if f:
  4081.                         _.MIXER = (f, args)
  4082.                         return None
  4083.                 except:
  4084.                     0
  4085.                     MIXERS
  4086.                     if traceback:
  4087.                         traceback.print_exc()
  4088.                     
  4089.  
  4090.             
  4091.             _.MIXER = None
  4092.         elif button == 3:
  4093.             _.app.opt = _.saved_opt
  4094.         
  4095.         if _.app.audio:
  4096.             _.app.audio.updateSettings()
  4097.             if button == 1:
  4098.                 _.app.audio.playSample('drop', priority = 1000)
  4099.             
  4100.         
  4101.         if button == 1:
  4102.             return EVENT_HANDLED
  4103.         
  4104.         return MfxDialog.mDone(_, button)
  4105.  
  4106.     
  4107.     def mCancel(_, *event):
  4108.         return _.mDone(2)
  4109.  
  4110.     
  4111.     def wmDeleteWindow(_, *event):
  4112.         return _.mDone(0)
  4113.  
  4114.     
  4115.     def mOptSoundDirectX(_, *event):
  4116.         d = MfxDialog(_.top, title = 'Sound info', text = 'Changing DirectX settings takes effect\nafter the next start of ' + PACKAGE, bitmap = 'info', default = 0, strings = ('OK',))
  4117.  
  4118.  
  4119.  
  4120. class AboutDialog(MfxDialog):
  4121.     
  4122.     def createFrames(_, kw):
  4123.         (top_frame, bottom_frame) = MfxDialog.createFrames(_, kw)
  4124.         return (top_frame, bottom_frame)
  4125.  
  4126.  
  4127.  
  4128. def helpAbout(app, timeout = 0, sound = 1):
  4129.     if sound:
  4130.         app.audio.playSample('about')
  4131.     
  4132.     t = 'A Python Solitaire Game Collection\n'
  4133.     if app.miscrandom.random() < 0.8 and os.name == 'posix':
  4134.         t = 'A World Domination Project\n'
  4135.     
  4136.     if PACKAGE == 'PyJongg':
  4137.         t = 'A Python Mahjongg Game Collection\n'
  4138.     
  4139.     d = AboutDialog(app.top, title = 'About ' + PACKAGE, timeout = timeout, text = PACKAGE + '\n' + t + 'Version ' + VERSION + '\n\n' + 'Copyright (C) 1998, 1999, 2000 Markus F.X.J. Oberhumer\n' + 'All Rights Reserved.\n\n' + PACKAGE + ' is free software distributed under the terms\n' + 'of the GNU General Public License\n\n' + 'For more information about this application visit\n' + PACKAGE_URL, image = app.gimages.logos[2], strings = ('Nice', 'Credits...'), default = 0, separatorwidth = 2)
  4140.     if d.status == 0 and d.button == 1:
  4141.         helpCredits(app, sound = sound)
  4142.     
  4143.     return d.status
  4144.  
  4145.  
  4146. def helpAboutSimple(app, timeout = 0, sound = 1):
  4147.     if sound:
  4148.         app.audio.playSample('about')
  4149.     
  4150.     t = 'A Solitaire Game Collection\n'
  4151.     if PACKAGE == 'PyJongg':
  4152.         t = 'A Python Mahjongg Game Collection\n'
  4153.     
  4154.     d = MfxDialog(app.top, title = 'About ' + PACKAGE, timeout = timeout, text = PACKAGE + '\n' + t + 'Version ' + VERSION + '\n\n' + 'Copyright (C) 1998, 1999, 2000\nMarkus F.X.J. Oberhumer\n' + '\nFor more information about this application visit\n' + PACKAGE_URL, image = app.gimages.logos[2], strings = ('Nice',), default = 0, separatorwidth = 2)
  4155.     return d.status
  4156.  
  4157.  
  4158. def helpCredits(app, timeout = 0, sound = 1):
  4159.     if sound:
  4160.         app.audio.playSample('credits')
  4161.     
  4162.     t = ''
  4163.     if tkname == 'tk':
  4164.         t = 'Tcl/Tk, '
  4165.     elif tkname == 'gnome':
  4166.         t = 'PyGTK, '
  4167.     elif tkname == 'kde':
  4168.         t = 'pyKDE, '
  4169.     elif tkname == 'wx':
  4170.         t = 'wxPython, '
  4171.     
  4172.     d = MfxDialog(app.top, title = PACKAGE + ' Credits', timeout = timeout, text = PACKAGE + ' credits go to:\n\n' + 'Volker Weidner for getting me into Solitaire\n' + 'Guido van Rossum for the initial example program\n' + 'T. Kirk for lots of contributed games and cardsets\n' + 'Carl Larsson for the background music\n' + 'The Gnome AisleRiot team for parts of the documentation\n' + '\n' + 'The Python, ' + t + 'SDL & Linux crews\nfor making this program possible', image = app.gimages.logos[3], image_side = 'right', separatorwidth = 2)
  4173.     return d.status
  4174.  
  4175. help_html_index = None
  4176.  
  4177. def helpHTML(app, document, dir):
  4178.     global help_html_index
  4179.     if not document:
  4180.         return None
  4181.     
  4182.     
  4183.     try:
  4184.         doc = app.dataloader.findFile(document, dir)
  4185.         if help_html_index is None:
  4186.             (document, dir) = ('index.html', 'html')
  4187.             help_html_index = app.dataloader.findFile(document, dir)
  4188.     except EnvError:
  4189.         d = MfxDialog(app.top, title = PACKAGE + ' HTML Problem', text = 'Cannot find help document\n' + document, bitmap = 'warning')
  4190.         return None
  4191.  
  4192.     top = makeHelpToplevel(app.top, title = PACKAGE + ' Help')
  4193.     if top.winfo_screenwidth() < 800 or top.winfo_screenheight() < 600:
  4194.         maximized = 1
  4195.         top.wm_minsize(300, 150)
  4196.     else:
  4197.         maximized = 0
  4198.         top.wm_minsize(400, 200)
  4199.     
  4200.     try:
  4201.         wm_set_icon(top, app.dataloader.findIcon())
  4202.     except:
  4203.         pass
  4204.  
  4205.     viewer = tkHTMLViewer(top)
  4206.     viewer.app = app
  4207.     viewer.home = help_html_index
  4208.     viewer.display(doc)
  4209.     wm_map(top, maximized = maximized)
  4210.     return viewer
  4211.  
  4212.  
  4213. class Status_StatsDialog(MfxDialog):
  4214.     
  4215.     def __init__(_, parent, game):
  4216.         (stats, gstats) = (game.stats, game.gstats)
  4217.         w1 = w2 = ''
  4218.         n = 0
  4219.         for s in game.s.foundations:
  4220.             n = n + len(s.cards)
  4221.         
  4222.         if PACKAGE == 'PyJongg':
  4223.             w1 = 'Highlight tiles: ' + str(stats.highlight_piles) + '\n'
  4224.             w2 = w2 + '\nTiles removed: ' + str(n)
  4225.             w2 = w2 + '\nTiles remaining: ' + str(len(game.cards) - n)
  4226.         else:
  4227.             w1 = 'Highlight piles: ' + str(stats.highlight_piles) + '\n' + 'Highlight cards: ' + str(stats.highlight_cards) + '\n' + 'Highlight same rank: ' + str(stats.highlight_samerank) + '\n'
  4228.             if game.s.waste and game.s.waste not in game.s.foundations:
  4229.                 w2 = w2 + '\nCards in Waste: ' + str(len(game.s.waste.cards))
  4230.             
  4231.             if game.s.foundations:
  4232.                 w2 = w2 + '\nCards in Foundations: ' + str(n)
  4233.             
  4234.         date = time.strftime('%Y-%m-%d %H:%M', time.localtime(game.gstats.start_time))
  4235.         MfxDialog.__init__(_, parent, title = 'Game status', text = game.getTitleName() + '\n' + game.getGameNumber(format = 1) + '\n' + 'Playing time: ' + game.getTime() + '\n' + 'Started at: ' + date + '\n\n' + 'Moves: ' + str(game.moves.index) + '\n' + 'Undo moves: ' + str(stats.undo_moves) + '\n' + 'Bookmark moves: ' + str(gstats.goto_bookmark_moves) + '\n' + 'Demo moves: ' + str(stats.demo_moves) + '\n\n' + 'Hints: ' + str(stats.hints) + '\n' + w1 + w2, strings = ('OK', ('Statistics...', 101)), image = game.app.gimages.logos[3], image_side = 'left', image_padx = 20, padx = 20, separatorwidth = 2)
  4236.  
  4237.  
  4238.  
  4239. class PysolStatsFormatter:
  4240.     
  4241.     def __init__(_, app):
  4242.         _.app = app
  4243.  
  4244.     
  4245.     class StringWriter:
  4246.         
  4247.         def __init__(_):
  4248.             _.text = ''
  4249.  
  4250.         
  4251.         def p(_, s):
  4252.             _.text = _.text + s
  4253.  
  4254.         
  4255.         def nl(_, count = 1):
  4256.             _.p('\n' * count)
  4257.  
  4258.         
  4259.         def pheader(_, s):
  4260.             _.p(s)
  4261.  
  4262.         
  4263.         def pstats(_, t1, t2, t3, t4, t5, gameid = None):
  4264.             s = '%-30s %7s %7s %7s %7s\n' % (t1, t2, t3, t4, t5)
  4265.             _.p(s)
  4266.  
  4267.         
  4268.         def plog(_, gamename, gamenumber, date, status, gameid = -1, won = -1):
  4269.             _.p('%-25s %-20s  %17s  %s\n' % (gamename, gamenumber, date, status))
  4270.  
  4271.  
  4272.     
  4273.     class FileWriter(StringWriter):
  4274.         
  4275.         def __init__(_, file):
  4276.             _.file = file
  4277.  
  4278.         
  4279.         def p(_, s):
  4280.             _.file.write(s)
  4281.  
  4282.  
  4283.     
  4284.     def writeHeader(_, writer, header, pagewidth = 72):
  4285.         date = time.ctime(time.time())
  4286.         date = time.strftime('%Y-%m-%d  %H:%M', time.localtime(time.time()))
  4287.         blanks = max(pagewidth - len(header) - len(date), 1)
  4288.         writer.pheader(header + ' ' * blanks + date + '\n')
  4289.         writer.pheader('-' * pagewidth + '\n')
  4290.         writer.pheader('\n')
  4291.  
  4292.     
  4293.     def writeStats(_, writer, player, header):
  4294.         app = _.app
  4295.         _.writeHeader(writer, header, 62)
  4296.         if not player:
  4297.             pass
  4298.         writer.pstats('Demo games', 'Played', 'Won', 'Lost', '% won')
  4299.         writer.nl()
  4300.         (twon, tlost, tgames) = (0, 0, 0)
  4301.         g = app.getGamesIdSortedByName()
  4302.         for id in g:
  4303.             name = app.getGameMenuitemName(id)
  4304.             (won, lost) = app.stats.getStats(player, id)
  4305.             (twon, tlost) = (twon + won, tlost + lost)
  4306.             if won > 0 and lost > 0 or id == app.game.id:
  4307.                 writer.pstats(name, won + lost, won, lost, perc, gameid = id)
  4308.                 tgames = tgames + 1
  4309.             
  4310.         
  4311.         writer.nl()
  4312.         (won, lost) = (twon, tlost)
  4313.         if won + lost > 0:
  4314.             perc = '%.1f' % 100.0 * won / (won + lost)
  4315.         else:
  4316.             perc = '0.0'
  4317.         writer.pstats('Total (%d out of %d games)' % (tgames, len(g)), won + lost, won, lost, perc)
  4318.         writer.nl(2)
  4319.         return tgames
  4320.  
  4321.     
  4322.     def _writeLog(_, writer, player, header, prev_games):
  4323.         if not player or not prev_games:
  4324.             return 0
  4325.         
  4326.         _.writeHeader(writer, header, 71)
  4327.         writer.plog('Game', 'Game number', 'Started at   ', 'Status')
  4328.         writer.nl()
  4329.         (twon, tlost) = (0, 0)
  4330.         for pg in prev_games:
  4331.             if len(pg) == 5:
  4332.                 pg = pg + ('', None, None, 1)
  4333.             elif len(pg) == 7:
  4334.                 pg = pg + (None, 1)
  4335.             elif len(pg) == 8:
  4336.                 pg = pg + (1,)
  4337.             
  4338.             if len(pg) < 8:
  4339.                 continue
  4340.             
  4341.             gameid = pg[0]
  4342.             if type(gameid) is not types.IntType:
  4343.                 continue
  4344.             
  4345.             gi = _.app.getGameInfo(gameid)
  4346.             if not gi:
  4347.                 gi = _.app.getGameInfo(GI.PROTECTED_GAMES.get(gameid))
  4348.             
  4349.             if gi:
  4350.                 name = gi.short_name
  4351.             else:
  4352.                 name = '** UNKNOWN %d **' % gameid
  4353.             f = pg[1]
  4354.             if len(f) == 16:
  4355.                 gamenumber = '%s-%s-%s' % (f[4:8], f[8:12], f[12:16])
  4356.             elif len(f) <= 20:
  4357.                 gamenumber = f
  4358.             else:
  4359.                 gamenumber = '** ERROR **'
  4360.             date = time.strftime('%Y-%m-%d  %H:%M', time.localtime(pg[3]))
  4361.             if pg[2] >= 0:
  4362.                 won = pg[2] > 0
  4363.                 (twon, tlost) = (twon + won, tlost + (1 - won))
  4364.             
  4365.             status = '*error*'
  4366.             if pg[2] <= pg[2]:
  4367.                 pass
  4368.             elif pg[2] <= 2:
  4369.                 status = ('Loaded', 'Not won', 'Lost', 'Won', 'Perfect')[pg[2] + 2]
  4370.             
  4371.             writer.plog(name, gamenumber, date, status, gameid = gameid, won = pg[2])
  4372.         
  4373.         writer.nl(2)
  4374.         return 1
  4375.  
  4376.     
  4377.     def writeFullLog(_, writer, player, header):
  4378.         prev_games = _.app.stats.prev_games.get(player)
  4379.         return _._writeLog(writer, player, header, prev_games)
  4380.  
  4381.     
  4382.     def writeSessionLog(_, writer, player, header):
  4383.         prev_games = _.app.stats.session_games.get(player)
  4384.         return _._writeLog(writer, player, header, prev_games)
  4385.  
  4386.  
  4387.  
  4388. class PysolMenubarActions:
  4389.     
  4390.     def __init__(_, app, top):
  4391.         _.app = app
  4392.         _.top = top
  4393.         _.game = None
  4394.         _.menustate = Struct(save = 0, save_as = 0, hold_and_quit = 0, undo = 0, redo = 0, restart = 0, deal = 0, hint = 0, autofaceup = 0, autodrop = 0, autodeal = 0, quickplay = 0, demo = 0, highlight_piles = 0, rules = 0)
  4395.         _.tkopt = Struct(gameid = MfxRadioMenuItem(_), gameid_popular = MfxRadioMenuItem(_), comment = MfxCheckMenuItem(_), autofaceup = MfxCheckMenuItem(_), autodrop = MfxCheckMenuItem(_), autodeal = MfxCheckMenuItem(_), quickplay = MfxCheckMenuItem(_), undo = MfxCheckMenuItem(_), bookmarks = MfxCheckMenuItem(_), hint = MfxCheckMenuItem(_), highlight_piles = MfxCheckMenuItem(_), highlight_cards = MfxCheckMenuItem(_), highlight_samerank = MfxCheckMenuItem(_), sound = MfxCheckMenuItem(_), cardback = MfxRadioMenuItem(_), tabletile = MfxRadioMenuItem(_), animations = MfxRadioMenuItem(_), shadow = MfxCheckMenuItem(_), shade = MfxCheckMenuItem(_), toolbar = MfxRadioMenuItem(_), toolbar_size = MfxRadioMenuItem(_), toolbar_relief = MfxRadioMenuItem(_), statusbar = MfxCheckMenuItem(_))
  4396.  
  4397.     
  4398.     def connectGame(_, game):
  4399.         _.game = game
  4400.         if game is None:
  4401.             return None
  4402.         
  4403.         if not __debug__ and _.app is game.app:
  4404.             raise AssertionError
  4405.         (tkopt, opt) = (_.tkopt, _.app.opt)
  4406.         tkopt.gameid.set(game.id)
  4407.         tkopt.gameid_popular.set(game.id)
  4408.         tkopt.comment.set(bool(game.gsaveinfo.comment))
  4409.         tkopt.autofaceup.set(opt.autofaceup)
  4410.         tkopt.autodrop.set(opt.autodrop)
  4411.         tkopt.autodeal.set(opt.autodeal)
  4412.         tkopt.quickplay.set(opt.quickplay)
  4413.         tkopt.undo.set(opt.undo)
  4414.         tkopt.hint.set(opt.hint)
  4415.         tkopt.bookmarks.set(opt.bookmarks)
  4416.         tkopt.highlight_piles.set(opt.highlight_piles)
  4417.         tkopt.highlight_cards.set(opt.highlight_cards)
  4418.         tkopt.highlight_samerank.set(opt.highlight_samerank)
  4419.         tkopt.sound.set(opt.sound)
  4420.         tkopt.cardback.set(_.app.cardset.backindex)
  4421.         (tkopt.tabletile.set(_.app.tabletile_index),)
  4422.         tkopt.animations.set(opt.animations)
  4423.         tkopt.shadow.set(opt.shadow)
  4424.         tkopt.shade.set(opt.shade)
  4425.         tkopt.toolbar.set(opt.toolbar)
  4426.         tkopt.toolbar_size.set(opt.toolbar_size)
  4427.         tkopt.toolbar_relief.set(opt.toolbar_relief)
  4428.         tkopt.statusbar.set(opt.statusbar)
  4429.  
  4430.     
  4431.     def updateRecentGamesMenu(_, gameids):
  4432.         pass
  4433.  
  4434.     
  4435.     def updateBookmarkMenuState(_):
  4436.         pass
  4437.  
  4438.     
  4439.     def updateBackgroundImagesMenu(_):
  4440.         pass
  4441.  
  4442.     
  4443.     def _finishDrag(_):
  4444.         if not _.game is None:
  4445.             pass
  4446.         return _.game._finishDrag()
  4447.  
  4448.     
  4449.     def _cancelDrag(_):
  4450.         if not _.game is None:
  4451.             pass
  4452.         return _.game._cancelDrag()
  4453.  
  4454.     
  4455.     def changed(_, *args, **kw):
  4456.         if not __debug__ and _.game is not None:
  4457.             raise AssertionError
  4458.         return apply(_.game.changed, args, kw)
  4459.  
  4460.     
  4461.     def setMenuState(_, state, path):
  4462.         raise SubclassResponsibility
  4463.  
  4464.     
  4465.     def setToolbarState(_, state, path):
  4466.         raise SubclassResponsibility
  4467.  
  4468.     
  4469.     def _clearMenuState(_):
  4470.         ms = _.menustate
  4471.         for k, v in ms.__dict__.items():
  4472.             pass
  4473.         
  4474.  
  4475.     
  4476.     def _updateMenuState(_):
  4477.         _._clearMenuState()
  4478.         game = _.game
  4479.         if not __debug__ and game is not None:
  4480.             raise AssertionError
  4481.         opt = _.app.opt
  4482.         ms = _.menustate
  4483.         ms.save_as = game.canSaveGame()
  4484.         ms.hold_and_quit = ms.save_as
  4485.         if game.filename and ms.save_as:
  4486.             ms.save = 1
  4487.         
  4488.         if opt.undo:
  4489.             if game.canUndo() and game.moves.index > 0:
  4490.                 ms.undo = 1
  4491.             
  4492.             if game.canRedo() and game.moves.index < len(game.moves.history):
  4493.                 ms.redo = 1
  4494.             
  4495.         
  4496.         if game.moves.index > 0:
  4497.             ms.restart = 1
  4498.         
  4499.         if game.canDealCards():
  4500.             ms.deal = 1
  4501.         
  4502.         if game.getHintClass() is not None:
  4503.             if opt.hint:
  4504.                 ms.hint = 1
  4505.             
  4506.             ms.demo = 1
  4507.         
  4508.         autostacks = game.getAutoStacks()
  4509.         if autostacks[0]:
  4510.             ms.autofaceup = 1
  4511.         
  4512.         if autostacks[1] and game.s.foundations:
  4513.             ms.autodrop = 1
  4514.         
  4515.         if game.s.waste:
  4516.             ms.autodeal = 1
  4517.         
  4518.         if autostacks[2]:
  4519.             ms.quickplay = 1
  4520.         
  4521.         ms.highlight_piles = 0
  4522.         if opt.highlight_piles and game.getHighlightPilesStacks():
  4523.             ms.highlight_piles = 1
  4524.         
  4525.         if game.app.getGameRulesFilename(game.id):
  4526.             ms.rules = 1
  4527.         
  4528.  
  4529.     
  4530.     def _updateMenus(_):
  4531.         if _.game is None:
  4532.             return None
  4533.         
  4534.         ms = _.menustate
  4535.         _.setMenuState(ms.save, 'file.save')
  4536.         _.setMenuState(ms.save_as, 'file.saveas')
  4537.         _.setMenuState(ms.hold_and_quit, 'file.holdandquit')
  4538.         _.setMenuState(ms.undo, 'edit.undo')
  4539.         _.setMenuState(ms.redo, 'edit.redo')
  4540.         _.setMenuState(ms.redo, 'edit.redoall')
  4541.         _.updateBookmarkMenuState()
  4542.         _.setMenuState(ms.restart, 'edit.restartgame')
  4543.         _.setMenuState(ms.deal, 'game.dealcards')
  4544.         _.setMenuState(ms.autodrop, 'game.autodrop')
  4545.         _.setMenuState(ms.hint, 'assist.hint')
  4546.         _.setMenuState(ms.highlight_piles, 'assist.highlightpiles')
  4547.         _.setMenuState(ms.demo, 'assist.demo')
  4548.         _.setMenuState(ms.demo, 'assist.demoallgames')
  4549.         _.setMenuState(ms.autofaceup, 'options.automaticplay.autofaceup')
  4550.         _.setMenuState(ms.autodrop, 'options.automaticplay.autodrop')
  4551.         _.setMenuState(ms.autodeal, 'options.automaticplay.autodeal')
  4552.         _.setMenuState(ms.quickplay, 'options.automaticplay.quickplay')
  4553.         _.setMenuState(ms.rules, 'help.rulesforthisgame')
  4554.         _.setToolbarState(ms.restart, 'restart')
  4555.         _.setToolbarState(ms.save_as, 'save')
  4556.         _.setToolbarState(ms.undo, 'undo')
  4557.         _.setToolbarState(ms.redo, 'redo')
  4558.         _.setToolbarState(ms.autodrop, 'autodrop')
  4559.         _.setToolbarState(ms.rules, 'rules')
  4560.         _.tkopt.comment.set(bool(_.game.gsaveinfo.comment))
  4561.  
  4562.     
  4563.     def updateMenus(_):
  4564.         if _.game is None:
  4565.             return None
  4566.         
  4567.         _._updateMenuState()
  4568.         _._updateMenus()
  4569.  
  4570.     
  4571.     def disableMenus(_):
  4572.         if _.game is None:
  4573.             return None
  4574.         
  4575.         _._clearMenuState()
  4576.         _._updateMenus()
  4577.  
  4578.     
  4579.     def mNewGame(_, *args):
  4580.         if _._cancelDrag():
  4581.             return None
  4582.         
  4583.         if _.changed():
  4584.             if not _.game.areYouSure('New game'):
  4585.                 return None
  4586.             
  4587.         
  4588.         if _.game.nextGameFlags(_.game.id) == 0:
  4589.             _.game.endGame()
  4590.             _.game.newGame()
  4591.         else:
  4592.             _.game.endGame()
  4593.             _.game.quitGame(_.game.id)
  4594.  
  4595.     
  4596.     def _mSelectGame(_, id, random = None):
  4597.         if _._cancelDrag():
  4598.             return None
  4599.         
  4600.         if _.game.id == id:
  4601.             return None
  4602.         
  4603.         if _.changed():
  4604.             if not _.game.areYouSure('Select game'):
  4605.                 _.tkopt.gameid.set(_.game.id)
  4606.                 _.tkopt.gameid_popular.set(_.game.id)
  4607.                 return None
  4608.             
  4609.         
  4610.         _.game.endGame()
  4611.         _.game.quitGame(id, random = random)
  4612.  
  4613.     
  4614.     def mSelectGame(_, *args):
  4615.         _._mSelectGame(_.tkopt.gameid.get())
  4616.  
  4617.     
  4618.     def mSelectGamePopular(_, *args):
  4619.         _._mSelectGame(_.tkopt.gameid_popular.get())
  4620.  
  4621.     
  4622.     def _mNewGameBySeed(_, seed, origin):
  4623.         
  4624.         try:
  4625.             (id, random) = constructRandom(seed)
  4626.             if id is None:
  4627.                 id = _.game.id
  4628.             
  4629.             if random is None:
  4630.                 return None
  4631.             
  4632.             if not _.app.getGameInfo(id):
  4633.                 raise ValueError
  4634.         except (ValueError, TypeError):
  4635.             ex = None
  4636.             d = MfxDialog(_.top, title = 'Invalid game number', text = 'Invalid game number\n' + str(seed), bitmap = 'error')
  4637.             return None
  4638.  
  4639.         f = _.game.nextGameFlags(id, random)
  4640.         if f & 17 == 0:
  4641.             return None
  4642.         
  4643.         random.origin = origin
  4644.         if f & 15 == 0:
  4645.             _.game.endGame()
  4646.             _.game.newGame(random = random)
  4647.         else:
  4648.             _.game.endGame()
  4649.             _.game.quitGame(id, random = random)
  4650.  
  4651.     
  4652.     def mNewGameWithNextId(_, *args):
  4653.         if _.changed():
  4654.             if not _.game.areYouSure('Select next game number'):
  4655.                 return None
  4656.             
  4657.         
  4658.         r = _.game.random
  4659.         seed = r.increaseSeed(r.initial_seed)
  4660.         seed = r.str(seed)
  4661.         _._mNewGameBySeed(seed, _.game.random.ORIGIN_NEXT_GAME)
  4662.  
  4663.     
  4664.     def mSelectGameById(_, *args):
  4665.         if _._cancelDrag():
  4666.             return None
  4667.         
  4668.         (id, f) = (None, _.game.getGameNumber(format = 0))
  4669.         d = MfxSimpleEntry(_.top, 'Select new game number', '\n\nEnter new game number', f, strings = ('OK', 'Next number', 'Cancel'), default = 0, e_width = 25)
  4670.         if d.status != 0:
  4671.             return None
  4672.         
  4673.         if d.button == 2:
  4674.             return None
  4675.         
  4676.         if d.button == 1:
  4677.             _.mNewGameWithNextId()
  4678.             return None
  4679.         
  4680.         if _.changed():
  4681.             if not _.game.areYouSure('Select new game number'):
  4682.                 return None
  4683.             
  4684.         
  4685.         _._mNewGameBySeed(d.value, _.game.random.ORIGIN_SELECTED)
  4686.  
  4687.     
  4688.     def mSelectRandomGame(_, *args):
  4689.         if _._cancelDrag():
  4690.             return None
  4691.         
  4692.         if _.changed():
  4693.             if not _.game.areYouSure('Select random game'):
  4694.                 return None
  4695.             
  4696.         
  4697.         for i in range(1000):
  4698.             gi = _.app.getGameInfo(_.app.getRandomGameId())
  4699.             if 1 and gi.id == _.game.id:
  4700.                 continue
  4701.             
  4702.             if 1 and gi.category != _.game.gameinfo.category:
  4703.                 continue
  4704.             
  4705.         
  4706.         if gi and gi.id != _.game.id:
  4707.             _.game.endGame()
  4708.             _.game.quitGame(gi.id)
  4709.         
  4710.  
  4711.     
  4712.     def _mSelectNextGameFromList(_, gl, step):
  4713.         if _._cancelDrag():
  4714.             return None
  4715.         
  4716.         id = _.game.id
  4717.         gl = list(gl)
  4718.         if len(gl) < 2 or not (id in gl):
  4719.             return None
  4720.         
  4721.         if _.changed():
  4722.             if not _.game.areYouSure('Select next game'):
  4723.                 return None
  4724.             
  4725.         
  4726.         index = (gl.index(id) + step) % len(gl)
  4727.         _.game.endGame()
  4728.         _.game.quitGame(gl[index])
  4729.  
  4730.     
  4731.     def mSelectNextGameById(_, *args):
  4732.         _._mSelectNextGameFromList(_.app.gdb.getGamesIdSortedById(), 1)
  4733.  
  4734.     
  4735.     def mSelectPrevGameById(_, *args):
  4736.         _._mSelectNextGameFromList(_.app.gdb.getGamesIdSortedById(), -1)
  4737.  
  4738.     
  4739.     def mSelectNextGameByName(_, *args):
  4740.         _._mSelectNextGameFromList(_.app.gdb.getGamesIdSortedByName(), 1)
  4741.  
  4742.     
  4743.     def mSelectPrevGameByName(_, *args):
  4744.         _._mSelectNextGameFromList(_.app.gdb.getGamesIdSortedByName(), -1)
  4745.  
  4746.     
  4747.     def mSave(_, *args):
  4748.         if _._cancelDrag():
  4749.             return None
  4750.         
  4751.         if _.menustate.save_as:
  4752.             if _.game.filename:
  4753.                 _.game.saveGame(_.game.filename)
  4754.             else:
  4755.                 _.mSaveAs()
  4756.         
  4757.  
  4758.     
  4759.     def mHoldAndQuit(_, *args):
  4760.         if _._cancelDrag():
  4761.             return None
  4762.         
  4763.         _.game.endGame(holdgame = 1)
  4764.         _.game.quitGame(holdgame = 1)
  4765.  
  4766.     
  4767.     def mQuit(_, *args):
  4768.         if _._cancelDrag():
  4769.             return None
  4770.         
  4771.         if _.changed():
  4772.             if not _.game.areYouSure('Quit PySol'):
  4773.                 return None
  4774.             
  4775.         
  4776.         _.game.endGame()
  4777.         _.game.quitGame()
  4778.  
  4779.     
  4780.     def mUndo(_, *args):
  4781.         if _._cancelDrag():
  4782.             return None
  4783.         
  4784.         if _.menustate.undo:
  4785.             _.game.playSample('undo')
  4786.             _.game.undo()
  4787.         
  4788.  
  4789.     
  4790.     def mRedo(_, *args):
  4791.         if _._cancelDrag():
  4792.             return None
  4793.         
  4794.         if _.menustate.redo:
  4795.             _.game.playSample('redo')
  4796.             _.game.redo()
  4797.             _.game.checkForWin()
  4798.         
  4799.  
  4800.     
  4801.     def mRedoAll(_, *args):
  4802.         if _._cancelDrag():
  4803.             return None
  4804.         
  4805.         if _.menustate.redo:
  4806.             _.game.playSample('redo', loop = 1)
  4807.             while _.game.moves.index < len(_.game.moves.history):
  4808.                 _.game.redo()
  4809.                 if _.game.checkForWin():
  4810.                     break
  4811.                 
  4812.             _.game.stopSamples()
  4813.         
  4814.  
  4815.     
  4816.     def mSetBookmark(_, n, confirm = 1):
  4817.         if _._cancelDrag():
  4818.             return None
  4819.         
  4820.         if not (_.app.opt.bookmarks):
  4821.             return None
  4822.         
  4823.         if not None if n <= n else n <= 8:
  4824.             return None
  4825.         
  4826.         _.game.setBookmark(n, confirm = confirm)
  4827.         _.game.updateMenus()
  4828.  
  4829.     
  4830.     def mGotoBookmark(_, n, confirm = -1):
  4831.         if _._cancelDrag():
  4832.             return None
  4833.         
  4834.         if not (_.app.opt.bookmarks):
  4835.             return None
  4836.         
  4837.         if not None if n <= n else n <= 8:
  4838.             return None
  4839.         
  4840.         _.game.gotoBookmark(n, confirm = confirm)
  4841.         _.game.updateMenus()
  4842.  
  4843.     
  4844.     def mClearBookmarks(_, *args):
  4845.         if _._cancelDrag():
  4846.             return None
  4847.         
  4848.         if not (_.app.opt.bookmarks):
  4849.             return None
  4850.         
  4851.         if not (_.game.gsaveinfo.bookmarks):
  4852.             return None
  4853.         
  4854.         if not _.game.areYouSure('Clear bookmarks', 'Clear all bookmarks ?'):
  4855.             return None
  4856.         
  4857.         _.game.gsaveinfo.bookmarks = { }
  4858.         _.game.updateMenus()
  4859.  
  4860.     
  4861.     def mRestart(_, *args):
  4862.         if _._cancelDrag():
  4863.             return None
  4864.         
  4865.         if _.game.moves.index == 0:
  4866.             return None
  4867.         
  4868.         if _.changed(restart = 1):
  4869.             if not _.game.areYouSure('Restart game', 'Restart this game ?'):
  4870.                 return None
  4871.             
  4872.         
  4873.         _.game.restartGame()
  4874.  
  4875.     
  4876.     def mDeal(_, *args):
  4877.         if _._cancelDrag():
  4878.             return None
  4879.         
  4880.         _.game.dealCards()
  4881.  
  4882.     
  4883.     def mDrop(_, *args):
  4884.         if _._cancelDrag():
  4885.             return None
  4886.         
  4887.         _.game.autoPlay(autofaceup = -1, autodrop = 1)
  4888.  
  4889.     
  4890.     def mDrop1(_, *args):
  4891.         if _._cancelDrag():
  4892.             return None
  4893.         
  4894.         _.game.autoPlay(autofaceup = 1, autodrop = 1)
  4895.  
  4896.     
  4897.     def mStatus(_, *args):
  4898.         if _._cancelDrag():
  4899.             return None
  4900.         
  4901.         _.mPlayerStats(mode = 100)
  4902.  
  4903.     
  4904.     def mEditGameComment(_, *args):
  4905.         if _._cancelDrag():
  4906.             return None
  4907.         
  4908.         (game, gi) = (_.game, _.game.gameinfo)
  4909.         t = ' ' + game.getGameNumber(format = 1)
  4910.         cc = 'Comments for ' + gi.name + t + ':\n\n'
  4911.         if not game.gsaveinfo.comment:
  4912.             pass
  4913.         c = cc
  4914.         d = EditTextDialog(game.top, 'Comments for' + t, text = c)
  4915.         if d.status == 0 and d.button == 0:
  4916.             if string.strip(d.text) == string.strip(cc):
  4917.                 game.gsaveinfo.comment = ''
  4918.             else:
  4919.                 game.gsaveinfo.comment = string.rstrip(d.text)
  4920.         
  4921.         _.tkopt.comment.set(bool(game.gsaveinfo.comment))
  4922.  
  4923.     
  4924.     def _mStatsSave(_, player, header, filename, write_method):
  4925.         file = None
  4926.         if player is None:
  4927.             text = 'Demo statistics'
  4928.             filename = filename + '_demo'
  4929.         else:
  4930.             text = 'Your statistics'
  4931.         filename = os.path.join(_.app.dn.config, filename + '.txt')
  4932.         filename = os.path.normpath(filename)
  4933.         
  4934.         try:
  4935.             file = open(filename, 'a')
  4936.             a = PysolStatsFormatter(_.app)
  4937.             writer = a.FileWriter(file)
  4938.             apply(write_method, (a, writer, player, header))
  4939.             destruct(a)
  4940.         except EnvError:
  4941.             ex = None
  4942.             if file:
  4943.                 file.close()
  4944.             
  4945.             d = MfxExceptionDialog(_.top, ex, text = 'Error while writing to file')
  4946.  
  4947.         if file:
  4948.             file.close()
  4949.         
  4950.         d = MfxDialog(_.top, title = PACKAGE + ' Info', bitmap = 'info', text = text + ' were appended to\n\n' + filename)
  4951.  
  4952.     
  4953.     def mPlayerStats(_, *args, **kw):
  4954.         mode = kw.get('mode', 101)
  4955.         demo = 0
  4956.         while mode > 0:
  4957.             if mode > 1000:
  4958.                 demo = not demo
  4959.                 mode = mode % 1000
  4960.             
  4961.             d = Struct(status = -1, button = -1)
  4962.             if demo:
  4963.                 player = None
  4964.                 (p0, p1, p2) = (PACKAGE + ' Demo', PACKAGE + ' Demo ', '')
  4965.             else:
  4966.                 player = _.app.opt.player
  4967.                 (p0, p1, p2) = (player, '', ' for ' + player)
  4968.             n = _.game.gameinfo.short_name
  4969.             if mode == 100:
  4970.                 d = Status_StatsDialog(_.top, game = _.game)
  4971.             elif mode == 101:
  4972.                 header = p1 + 'Statistics for ' + n
  4973.                 d = SingleGame_StatsDialog(_.top, header, _.app, player, gameid = _.game.id)
  4974.             elif mode == 102:
  4975.                 header = p1 + 'Statistics' + p2
  4976.                 d = AllGames_StatsDialog(_.top, header, _.app, player)
  4977.             elif mode == 103:
  4978.                 header = p1 + 'Full log' + p2
  4979.                 d = FullLog_StatsDialog(_.top, header, _.app, player)
  4980.             elif mode == 104:
  4981.                 header = p1 + 'Session log' + p2
  4982.                 d = SessionLog_StatsDialog(_.top, header, _.app, player)
  4983.             elif mode == 202:
  4984.                 header = 'Statistics for ' + p0
  4985.                 write_method = PysolStatsFormatter.writeStats
  4986.                 _._mStatsSave(player, header, 'stats', write_method)
  4987.             elif mode == 203:
  4988.                 header = 'Full log for ' + p0
  4989.                 write_method = PysolStatsFormatter.writeFullLog
  4990.                 _._mStatsSave(player, header, 'log', write_method)
  4991.             elif mode == 204:
  4992.                 header = 'Session log for ' + p0
  4993.                 write_method = PysolStatsFormatter.writeSessionLog
  4994.                 _._mStatsSave(player, header, 'log', write_method)
  4995.             elif mode == 301:
  4996.                 if _.game.areYouSure('Reset all statistics', 'Reset ALL statistics and logs for player\n' + p0 + ' ?', confirm = 1, default = 1):
  4997.                     _.app.stats.resetStats(player, 0)
  4998.                     _.game.updateStatus(stats = _.app.stats.getStats(_.app.opt.player, _.game.id))
  4999.                 
  5000.             elif mode == 302:
  5001.                 if _.game.areYouSure('Reset game statistics', 'Reset statistics and logs for player\n' + p0 + '\nand game\n' + n + ' ?', confirm = 1, default = 1):
  5002.                     _.app.stats.resetStats(player, _.game.id)
  5003.                     _.game.updateStatus(stats = _.app.stats.getStats(_.app.opt.player, _.game.id))
  5004.                 
  5005.             elif mode == 401:
  5006.                 pass
  5007.             elif mode == 402:
  5008.                 pass
  5009.             else:
  5010.                 print 'stats problem:', mode, demo, player
  5011.             if d.status != 0:
  5012.                 break
  5013.             
  5014.             mode = d.button
  5015.  
  5016.     
  5017.     def mHint(_, *args):
  5018.         if _._cancelDrag():
  5019.             return None
  5020.         
  5021.         if _.app.opt.hint:
  5022.             if _.game.showHint(0, _.app.opt.hint_sleep):
  5023.                 _.game.stats.hints = _.game.stats.hints + 1
  5024.             
  5025.         
  5026.  
  5027.     
  5028.     def mHint1(_, *args):
  5029.         if _._cancelDrag():
  5030.             return None
  5031.         
  5032.         if _.app.opt.hint:
  5033.             if _.game.showHint(1, _.app.opt.hint_sleep):
  5034.                 _.game.stats.hints = _.game.stats.hints + 1
  5035.             
  5036.         
  5037.  
  5038.     
  5039.     def mHighlightPiles(_, *args):
  5040.         if _._cancelDrag():
  5041.             return None
  5042.         
  5043.         if _.app.opt.highlight_piles:
  5044.             if _.game.highlightPiles(_.app.opt.highlight_piles_sleep):
  5045.                 _.game.stats.highlight_piles = _.game.stats.highlight_piles + 1
  5046.             
  5047.         
  5048.  
  5049.     
  5050.     def mDemo(_, *args):
  5051.         if _._cancelDrag():
  5052.             return None
  5053.         
  5054.         if _.game.getHintClass() is not None:
  5055.             _._mDemo(mixed = 0)
  5056.         
  5057.  
  5058.     
  5059.     def mMixedDemo(_, *args):
  5060.         if _._cancelDrag():
  5061.             return None
  5062.         
  5063.         _._mDemo(mixed = 1)
  5064.  
  5065.     
  5066.     def _mDemo(_, mixed):
  5067.         if _._cancelDrag():
  5068.             return None
  5069.         
  5070.         if _.changed():
  5071.             if _.game.stats.demo_moves == 0 and _.game.stats.hints == 0:
  5072.                 if not _.game.areYouSure('Play demo'):
  5073.                     return None
  5074.                 
  5075.             
  5076.         
  5077.         _.game.startDemo(mixed = mixed)
  5078.  
  5079.     
  5080.     def mOptPlayerOptions(_, *args):
  5081.         if _._cancelDrag():
  5082.             return None
  5083.         
  5084.         d = PlayerOptionsDialog(_.top, 'Set player options', _.app)
  5085.         if d.status == 0 and d.button == 0:
  5086.             _.app.opt.confirm = bool(d.confirm)
  5087.             _.app.opt.update_player_stats = bool(d.update_stats)
  5088.             _.app.opt.win_animation = bool(d.win_animation)
  5089.             n = string.strip(d.player[:30])
  5090.             if len(n) < len(n):
  5091.                 pass
  5092.             elif len(n) <= 30:
  5093.                 _.app.opt.player = n
  5094.                 _.game.updateStatus(player = _.app.opt.player)
  5095.                 _.game.updateStatus(stats = _.app.stats.getStats(_.app.opt.player, _.game.id))
  5096.             
  5097.         
  5098.  
  5099.     
  5100.     def mOptAutoFaceUp(_, *args):
  5101.         if _._cancelDrag():
  5102.             return None
  5103.         
  5104.         _.app.opt.autofaceup = _.tkopt.autofaceup.get()
  5105.         if _.app.opt.autofaceup:
  5106.             _.game.autoPlay()
  5107.         
  5108.  
  5109.     
  5110.     def mOptAutoDrop(_, *args):
  5111.         if _._cancelDrag():
  5112.             return None
  5113.         
  5114.         _.app.opt.autodrop = _.tkopt.autodrop.get()
  5115.         if _.app.opt.autodrop:
  5116.             _.game.autoPlay()
  5117.         
  5118.  
  5119.     
  5120.     def mOptAutoDeal(_, *args):
  5121.         if _._cancelDrag():
  5122.             return None
  5123.         
  5124.         _.app.opt.autodeal = _.tkopt.autodeal.get()
  5125.         if _.app.opt.autodeal:
  5126.             _.game.autoPlay()
  5127.         
  5128.  
  5129.     
  5130.     def mOptQuickPlay(_, *args):
  5131.         if _._cancelDrag():
  5132.             return None
  5133.         
  5134.         _.app.opt.quickplay = _.tkopt.quickplay.get()
  5135.  
  5136.     
  5137.     def mOptEnableUndo(_, *args):
  5138.         if _._cancelDrag():
  5139.             return None
  5140.         
  5141.         _.app.opt.undo = _.tkopt.undo.get()
  5142.         _.game.updateMenus()
  5143.  
  5144.     
  5145.     def mOptEnableBookmarks(_, *args):
  5146.         if _._cancelDrag():
  5147.             return None
  5148.         
  5149.         _.app.opt.bookmarks = _.tkopt.bookmarks.get()
  5150.         _.game.updateMenus()
  5151.  
  5152.     
  5153.     def mOptEnableHint(_, *args):
  5154.         if _._cancelDrag():
  5155.             return None
  5156.         
  5157.         _.app.opt.hint = _.tkopt.hint.get()
  5158.         _.game.updateMenus()
  5159.  
  5160.     
  5161.     def mOptEnableHighlightPiles(_, *args):
  5162.         if _._cancelDrag():
  5163.             return None
  5164.         
  5165.         _.app.opt.highlight_piles = _.tkopt.highlight_piles.get()
  5166.         _.game.updateMenus()
  5167.  
  5168.     
  5169.     def mOptEnableHighlightCards(_, *args):
  5170.         if _._cancelDrag():
  5171.             return None
  5172.         
  5173.         _.app.opt.highlight_cards = _.tkopt.highlight_cards.get()
  5174.         _.game.updateMenus()
  5175.  
  5176.     
  5177.     def mOptEnableHighlightSameRank(_, *args):
  5178.         if _._cancelDrag():
  5179.             return None
  5180.         
  5181.         _.app.opt.highlight_samerank = _.tkopt.highlight_samerank.get()
  5182.         _.game.updateMenus()
  5183.  
  5184.     
  5185.     def mOptSound(_, *args):
  5186.         if _._cancelDrag():
  5187.             return None
  5188.         
  5189.         _.app.opt.sound = _.tkopt.sound.get()
  5190.         if not (_.app.opt.sound):
  5191.             _.app.audio.stopAll()
  5192.         
  5193.  
  5194.     
  5195.     def mOptSoundDialog(_, *args):
  5196.         if _._cancelDrag():
  5197.             return None
  5198.         
  5199.         d = SoundOptionsDialog(_.top, 'Sound settings', _.app)
  5200.         _.tkopt.sound.set(_.app.opt.sound)
  5201.  
  5202.     
  5203.     def mOptAnimations(_, *args):
  5204.         if _._cancelDrag():
  5205.             return None
  5206.         
  5207.         _.app.opt.animations = _.tkopt.animations.get()
  5208.  
  5209.     
  5210.     def mOptShadow(_, *args):
  5211.         if _._cancelDrag():
  5212.             return None
  5213.         
  5214.         _.app.opt.shadow = _.tkopt.shadow.get()
  5215.  
  5216.     
  5217.     def mOptShade(_, *args):
  5218.         if _._cancelDrag():
  5219.             return None
  5220.         
  5221.         _.app.opt.shade = _.tkopt.shade.get()
  5222.  
  5223.     
  5224.     def mOptIrregularPiles(_, *args):
  5225.         if _._cancelDrag():
  5226.             return None
  5227.         
  5228.         _.app.opt.irregular_piles = _.tkopt.irregular_piles.get()
  5229.  
  5230.     
  5231.     def mOptDemoOptions(_, *args):
  5232.         if _._cancelDrag():
  5233.             return None
  5234.         
  5235.         d = DemoOptionsDialog(_.top, 'Set demo options', _.app)
  5236.         if d.status == 0 and d.button == 0:
  5237.             _.app.opt.demo_logo = d.demo_logo
  5238.             _.app.opt.demo_score = d.demo_score
  5239.             _.app.opt.demo_sleep = d.demo_sleep
  5240.         
  5241.  
  5242.     
  5243.     def mOptHintOptions(_, *args):
  5244.         if _._cancelDrag():
  5245.             return None
  5246.         
  5247.         d = HintOptionsDialog(_.top, 'Set hint options', _.app)
  5248.         if d.status == 0 and d.button == 0:
  5249.             _.app.opt.hint_sleep = d.hint_sleep
  5250.         
  5251.  
  5252.     
  5253.     def mOptSave(_, *args):
  5254.         if _._cancelDrag():
  5255.             return None
  5256.         
  5257.         
  5258.         try:
  5259.             _.app.saveOptions()
  5260.         except Exception:
  5261.             ex = None
  5262.             d = MfxExceptionDialog(_.top, ex, text = 'Error while saving options')
  5263.  
  5264.         d = MfxDialog(_.top, title = PACKAGE + ' Info', text = 'Options were saved to\n\n' + _.app.fn.opt, bitmap = 'info')
  5265.  
  5266.     
  5267.     def mHelp(_, *args):
  5268.         if _._cancelDrag():
  5269.             return None
  5270.         
  5271.         helpHTML(_.app, 'index.html', 'html')
  5272.  
  5273.     
  5274.     def mHelpHowToPlay(_, *args):
  5275.         if _._cancelDrag():
  5276.             return None
  5277.         
  5278.         helpHTML(_.app, 'howtoplay.html', 'html')
  5279.  
  5280.     
  5281.     def mHelpRules(_, *args):
  5282.         if _._cancelDrag():
  5283.             return None
  5284.         
  5285.         if not (_.menustate.rules):
  5286.             return None
  5287.         
  5288.         dir = os.path.join('html', 'rules')
  5289.         helpHTML(_.app, _.app.getGameRulesFilename(_.game.id), dir)
  5290.  
  5291.     
  5292.     def mHelpLicense(_, *args):
  5293.         if _._cancelDrag():
  5294.             return None
  5295.         
  5296.         helpHTML(_.app, 'license.html', 'html')
  5297.  
  5298.     
  5299.     def mHelpNews(_, *args):
  5300.         if _._cancelDrag():
  5301.             return None
  5302.         
  5303.         helpHTML(_.app, 'news.html', 'html')
  5304.  
  5305.     
  5306.     def mHelpWebSite(_, *args):
  5307.         openURL(PACKAGE_URL)
  5308.  
  5309.     
  5310.     def mHelpAbout(_, *args):
  5311.         if _._cancelDrag():
  5312.             return None
  5313.         
  5314.         if _.app and bundle & 4:
  5315.             helpAboutSimple(_.app)
  5316.         else:
  5317.             helpAbout(_.app)
  5318.  
  5319.     
  5320.     def mScreenshot(_, *args):
  5321.         if _._cancelDrag():
  5322.             return None
  5323.         
  5324.         f = os.path.join(_.app.dn.config, 'screenshots')
  5325.         if not os.path.isdir(f):
  5326.             return None
  5327.         
  5328.         f = os.path.join(f, _.app.getGameSaveName(_.game.id))
  5329.         i = 1
  5330.         while 1:
  5331.             fn = f + '-%d.ppm' % i
  5332.             if not os.path.isfile(fn):
  5333.                 break
  5334.             
  5335.             i = i + 1
  5336.             if i >= 10000:
  5337.                 return None
  5338.             
  5339.         _.top.screenshot(fn)
  5340.  
  5341.     
  5342.     def mPlayNextMusic(_, *args):
  5343.         if _._cancelDrag():
  5344.             return None
  5345.         
  5346.         if _.app.audio and _.app.opt.sound_music_volume > 0:
  5347.             _.app.audio.playNextMusic()
  5348.             if 1 and _.app.debug:
  5349.                 index = _.app.audio.getMusicInfo()
  5350.                 music = _.app.music_manager.get(index)
  5351.                 if music:
  5352.                     print 'playing music:', music.filename
  5353.                 
  5354.             
  5355.         
  5356.  
  5357.  
  5358.  
  5359. class PysolToolbarActions:
  5360.     
  5361.     def __init__(_):
  5362.         _.game = None
  5363.         _.menubar = None
  5364.  
  5365.     
  5366.     def connectGame(_, game, menubar):
  5367.         _.game = game
  5368.         _.menubar = menubar
  5369.  
  5370.     
  5371.     def _busy(_):
  5372.         raise SubclassResponsibility
  5373.  
  5374.     
  5375.     def mNewGame(_, *args):
  5376.         if not _._busy():
  5377.             _.menubar.mNewGame()
  5378.         
  5379.         return 1
  5380.  
  5381.     
  5382.     def mOpen(_, *args):
  5383.         if not _._busy():
  5384.             _.menubar.mOpen()
  5385.         
  5386.         return 1
  5387.  
  5388.     
  5389.     def mRestart(_, *args):
  5390.         if not _._busy():
  5391.             _.menubar.mRestart()
  5392.         
  5393.         return 1
  5394.  
  5395.     
  5396.     def mSave(_, *args):
  5397.         if not _._busy():
  5398.             _.menubar.mSaveAs()
  5399.         
  5400.         return 1
  5401.  
  5402.     
  5403.     def mUndo(_, *args):
  5404.         if not _._busy():
  5405.             _.menubar.mUndo()
  5406.         
  5407.         return 1
  5408.  
  5409.     
  5410.     def mRedo(_, *args):
  5411.         if not _._busy():
  5412.             _.menubar.mRedo()
  5413.         
  5414.         return 1
  5415.  
  5416.     
  5417.     def mDrop(_, *args):
  5418.         if not _._busy():
  5419.             _.menubar.mDrop()
  5420.         
  5421.         return 1
  5422.  
  5423.     
  5424.     def mStatus(_, *args):
  5425.         if not _._busy():
  5426.             _.menubar.mStatus()
  5427.         
  5428.         return 1
  5429.  
  5430.     
  5431.     def mPlayerStats(_, *args):
  5432.         if not _._busy():
  5433.             _.menubar.mPlayerStats()
  5434.         
  5435.         return 1
  5436.  
  5437.     
  5438.     def mHelpRules(_, *args):
  5439.         if not _._busy():
  5440.             _.menubar.mHelpRules()
  5441.         
  5442.         return 1
  5443.  
  5444.     
  5445.     def mQuit(_, *args):
  5446.         if not _._busy():
  5447.             _.menubar.mQuit()
  5448.         
  5449.         return 1
  5450.  
  5451.     
  5452.     def mOptPlayerOptions(_, *args):
  5453.         if not _._busy():
  5454.             _.menubar.mOptPlayerOptions()
  5455.         
  5456.         return 1
  5457.  
  5458.  
  5459.  
  5460. class _MfxToolbar:
  5461.     
  5462.     def __init__(_, top, relief = 0):
  5463.         _.top = top
  5464.         _.button_bg = None
  5465.         _._MfxToolbar__setRelief(relief)
  5466.         _.side = -1
  5467.         _._tooltips = []
  5468.         _._widgets = []
  5469.         _._icon_height = 0
  5470.         _.canvas = Tkinter.Canvas(_.top, bd = 0, highlightthickness = 0)
  5471.         _.frame = Tkinter.Frame(_.canvas, bd = 0, highlightthickness = 1)
  5472.         _._initFrame()
  5473.         _.canvas.pack(side = Tkinter.TOP, fill = Tkinter.X)
  5474.  
  5475.     
  5476.     def _MfxToolbar__setRelief(_, relief):
  5477.         if type(relief) is types.IntType:
  5478.             relief = (Tkinter.RAISED, Tkinter.FLAT)[relief]
  5479.         elif relief in (Tkinter.RAISED, Tkinter.FLAT):
  5480.             pass
  5481.         else:
  5482.             relief = Tkinter.FLAT
  5483.         _.button_relief = relief
  5484.         if relief == Tkinter.RAISED:
  5485.             _.separator_relief = Tkinter.FLAT
  5486.         else:
  5487.             _.separator_relief = Tkinter.RAISED
  5488.         return relief
  5489.  
  5490.     
  5491.     def _initFrame(_):
  5492.         _.frame.pack(side = Tkinter.TOP, fill = Tkinter.X)
  5493.  
  5494.     
  5495.     def _createSeparator(_, width = 12, side = Tkinter.LEFT, relief = None):
  5496.         if relief is None:
  5497.             relief = _.separator_relief
  5498.         
  5499.         if relief == Tkinter.FLAT or width <= 6:
  5500.             sep = Tkinter.Frame(_.frame, highlightthickness = 0, width = width, height = 0, takefocus = 0, relief = relief)
  5501.             padx = 0
  5502.         elif not _._icon_height:
  5503.             pass
  5504.         height = max(38 - 4, 20)
  5505.         sep = Tkinter.Frame(_.frame, bd = 1, highlightthickness = 1, width = 4, height = height, takefocus = 0, relief = relief)
  5506.         padx = (width - 4) / 2
  5507.         sep.pack(side = side, padx = padx)
  5508.         _._widgets.append(sep)
  5509.  
  5510.     
  5511.     def show(_, side = 1, resize = 1):
  5512.         if _.side == side:
  5513.             return 0
  5514.         
  5515.         if resize:
  5516.             _.top.wm_geometry('')
  5517.         
  5518.         if not side:
  5519.             if 0 and TK_DASH_PATCH:
  5520.                 _.canvas.config(state = 'hidden')
  5521.             else:
  5522.                 _.canvas.pack_propagate(0)
  5523.                 _.canvas.config(height = 0)
  5524.         elif 0 and TK_DASH_PATCH:
  5525.             _.canvas.config(state = 'normal')
  5526.         else:
  5527.             _.canvas.pack_propagate(1)
  5528.         s = (None, Tkinter.TOP, Tkinter.BOTTOM)[side]
  5529.         _.canvas.pack(side = s, fill = Tkinter.X)
  5530.         _.side = side
  5531.         return 1
  5532.  
  5533.     
  5534.     def hide(_, resize = 1):
  5535.         _.show(0, resize)
  5536.  
  5537.     
  5538.     def getSide(_):
  5539.         return _.side
  5540.  
  5541.     
  5542.     def destroy(_):
  5543.         for w in _._tooltips:
  5544.             pass
  5545.         
  5546.         _._tooltips = []
  5547.         for w in _._widgets:
  5548.             pass
  5549.         
  5550.         _._widgets = []
  5551.  
  5552.     
  5553.     def setCursor(_, cursor):
  5554.         if _.side:
  5555.             _.frame.config(cursor = cursor)
  5556.             _.frame.update_idletasks()
  5557.         
  5558.  
  5559.  
  5560.  
  5561. class PysolToolbar(_MfxToolbar, PysolToolbarActions):
  5562.     
  5563.     def __init__(_, top, dir, size, relief = 0):
  5564.         _MfxToolbar.__init__(_, top, relief)
  5565.         PysolToolbarActions.__init__(_)
  5566.         _.dir = dir
  5567.         _.size = size
  5568.         _._createButton('new', _.mNewGame, tooltip = 'New game')
  5569.         _._createButton('open', _.mOpen, tooltip = 'Open a \nsaved game')
  5570.         _._createSeparator()
  5571.         _._createButton('restart', _.mRestart, tooltip = 'Restart the \ncurrent game')
  5572.         _._createButton('save', _.mSave, tooltip = 'Save game')
  5573.         _._createSeparator()
  5574.         _._createButton('undo', _.mUndo, tooltip = 'Undo last move')
  5575.         _._createButton('redo', _.mRedo, tooltip = 'Redo last move')
  5576.         _._createButton('autodrop', _.mDrop, tooltip = 'Auto drop cards')
  5577.         _._createSeparator()
  5578.         _._createButton('stats', _.mPlayerStats, tooltip = 'View statistics')
  5579.         _._createButton('rules', _.mHelpRules, tooltip = 'Rules for this game')
  5580.         _._createSeparator()
  5581.         _._createButton('quit', _.mQuit, tooltip = 'Quit PySol')
  5582.         _._createSeparator(width = 20, relief = Tkinter.FLAT)
  5583.         _._createLabel('player', padx = 8, tooltip = 'Change name of\ncurrent player')
  5584.         _.player_label.bind('<1>', _.mOptPlayerOptions)
  5585.         _.popup = None
  5586.         _.frame.bind('<1>', _.clickHandler)
  5587.         _.frame.bind('<3>', _.rightclickHandler)
  5588.  
  5589.     
  5590.     def _initFrame(_):
  5591.         if 0 or os.name == 'nt':
  5592.             _.frame.config(bd = 1, relief = Tkinter.SOLID)
  5593.             _.frame.pack(side = Tkinter.TOP, fill = Tkinter.X, ipady = 4)
  5594.             _._createSeparator(width = 4, side = Tkinter.LEFT, relief = Tkinter.FLAT)
  5595.             _._createSeparator(width = 4, side = Tkinter.RIGHT, relief = Tkinter.FLAT)
  5596.         else:
  5597.             _.frame.pack(side = Tkinter.TOP, fill = Tkinter.X, ipady = 1)
  5598.  
  5599.     
  5600.     def _loadImage(_, name):
  5601.         file = os.path.join(_.dir, name)
  5602.         image = None
  5603.         for ext in IMAGE_EXTENSIONS:
  5604.             file = os.path.join(_.dir, name + ext)
  5605.         
  5606.         return image
  5607.  
  5608.     
  5609.     def _createButton(_, name, command, padx = 0, tooltip = None):
  5610.         image = _._loadImage(name)
  5611.         button = Tkinter.Button(_.frame, command = command, takefocus = 0, relief = _.button_relief)
  5612.         button.toolbar_name = name
  5613.         if image:
  5614.             button.config(image = image)
  5615.             if _._icon_height == 0:
  5616.                 _._icon_height = image.height()
  5617.             
  5618.         
  5619.         if _.button_relief == Tkinter.FLAT:
  5620.             if _.button_bg is None:
  5621.                 _.button_bg = _._getButtonBg(button['activebackground'])
  5622.             
  5623.             button['activebackground'] = _.button_bg
  5624.         
  5625.         button.pack(side = Tkinter.LEFT, padx = padx)
  5626.         setattr(_, name + '_image', image)
  5627.         setattr(_, name + '_button', button)
  5628.         _._widgets.append(button)
  5629.         if tooltip:
  5630.             b = MfxTooltip(button)
  5631.             _._tooltips.append(b)
  5632.             b.setText(tooltip)
  5633.         
  5634.  
  5635.     
  5636.     def _getButtonBg(_, col):
  5637.         if type(col) is not types.StringType and col[0] != '#' or len(col) != 7:
  5638.             return '#f0f0f0'
  5639.         
  5640.         c = '#'
  5641.         for i in (1, 3, 5):
  5642.             v = string.atoi(col[i:i + 2], 16)
  5643.             v = int(v + 24)
  5644.             c = c + '%02x' % v
  5645.         
  5646.         return c
  5647.  
  5648.     
  5649.     def _createLabel(_, name, padx = 0, side = Tkinter.RIGHT, tooltip = None):
  5650.         aspect = (400, 300)[_.size != 0]
  5651.         label = Tkinter.Message(_.frame, relief = 'ridge', justify = 'center', aspect = aspect)
  5652.         label.pack(side = side, padx = padx)
  5653.         setattr(_, name + '_label', label)
  5654.         _._widgets.append(label)
  5655.         if tooltip:
  5656.             b = MfxTooltip(label)
  5657.             _._tooltips.append(b)
  5658.             b.setText(tooltip)
  5659.         
  5660.  
  5661.     
  5662.     def _busy(_):
  5663.         if not (_.side) and not (_.game) or not (_.menubar):
  5664.             return 1
  5665.         
  5666.         if _.game.demo:
  5667.             _.game.stopDemo()
  5668.         
  5669.         return _.game.busy
  5670.  
  5671.     
  5672.     def connectGame(_, game, menubar):
  5673.         PysolToolbarActions.connectGame(_, game, menubar)
  5674.         if _.popup:
  5675.             _.popup.destroy()
  5676.             destruct(_.popup)
  5677.             _.popup = None
  5678.         
  5679.         if menubar:
  5680.             tkopt = menubar.tkopt
  5681.             _.popup = Tkinter.Menu(_.canvas, tearoff = 0)
  5682.             _.popup.add_command(label = 'Toolbar', state = Tkinter.DISABLED)
  5683.             _.popup.add_radiobutton(label = 'Hide', variable = tkopt.toolbar, value = 0, command = menubar.mOptToolbar, underline = 0)
  5684.             _.popup.add_radiobutton(label = 'Top', variable = tkopt.toolbar, value = 1, command = menubar.mOptToolbar, underline = 0)
  5685.             _.popup.add_radiobutton(label = 'Bottom', variable = tkopt.toolbar, value = 2, command = menubar.mOptToolbar, underline = 0)
  5686.             if 1:
  5687.                 _.popup.add_separator()
  5688.                 _.popup.add_radiobutton(label = 'Small icons', variable = tkopt.toolbar_size, value = 0, command = menubar.mOptToolbarSize, underline = 0)
  5689.                 _.popup.add_radiobutton(label = 'Large icons', variable = tkopt.toolbar_size, value = 1, command = menubar.mOptToolbarSize, underline = 0)
  5690.             
  5691.         
  5692.  
  5693.     
  5694.     def setRelief(_, relief):
  5695.         pass
  5696.  
  5697.     
  5698.     def updateText(_, **kw):
  5699.         for name in kw.keys():
  5700.             label = getattr(_, name + '_label')
  5701.             label['text'] = str(kw[name])
  5702.         
  5703.  
  5704.     
  5705.     def updateImages(_, dir, size):
  5706.         if dir == _.dir and size == _.size:
  5707.             return 0
  5708.         
  5709.         if not os.path.isdir(dir):
  5710.             return 0
  5711.         
  5712.         (old_dir, old_size) = (_.dir, _.size)
  5713.         (_.dir, _.size) = (dir, size)
  5714.         data = []
  5715.         
  5716.         try:
  5717.             l = _.player_label
  5718.             for w in _._widgets:
  5719.                 name = w.toolbar_name
  5720.                 image = _._loadImage(name)
  5721.                 data.append((name, w, image))
  5722.         except:
  5723.             (_.dir, _.size) = (old_dir, old_size)
  5724.             return 0
  5725.  
  5726.         aspect = (400, 300)[size != 0]
  5727.         l.config(aspect = aspect)
  5728.         for name, w, image in data:
  5729.             w.config(image = image)
  5730.             setattr(_, name + '_image', image)
  5731.         
  5732.         return 1
  5733.  
  5734.     
  5735.     def clickHandler(_, event):
  5736.         if _._busy():
  5737.             return EVENT_HANDLED
  5738.         
  5739.         return EVENT_HANDLED
  5740.  
  5741.     
  5742.     def rightclickHandler(_, event):
  5743.         if _._busy():
  5744.             return EVENT_HANDLED
  5745.         
  5746.         if _.popup:
  5747.             _.popup.tk_popup(event.x_root, event.y_root)
  5748.         
  5749.         return EVENT_HANDLED
  5750.  
  5751.     
  5752.     def middleclickHandler(_, event):
  5753.         if _._busy():
  5754.             return EVENT_HANDLED
  5755.         
  5756.         if _.side <= _.side:
  5757.             pass
  5758.         elif _.side <= 2:
  5759.             _.menubar.setToolbarSide(3 - _.side)
  5760.         
  5761.         return EVENT_HANDLED
  5762.  
  5763.  
  5764.  
  5765. class _MfxStatusbar:
  5766.     
  5767.     def __init__(_, top):
  5768.         _.top = top
  5769.         _.side = '#init#'
  5770.         _._widgets = []
  5771.         _._tooltips = []
  5772.         _.padx = 0
  5773.         _.pady = 0
  5774.         _.canvas = Tkinter.Canvas(_.top, bd = 0, highlightthickness = 0)
  5775.         _.frame = Tkinter.Frame(_.canvas, bd = 1, relief = Tkinter.RAISED)
  5776.         _.frame.pack(side = Tkinter.TOP, fill = Tkinter.X, pady = _.pady)
  5777.         _.canvas.pack(side = Tkinter.BOTTOM, fill = Tkinter.X)
  5778.  
  5779.     
  5780.     def _createLabel(_, name, text = '', relief = Tkinter.SUNKEN, side = Tkinter.LEFT, fill = Tkinter.NONE, padx = -1, expand = 0, tooltip = None):
  5781.         if padx < 0:
  5782.             padx = _.padx
  5783.         
  5784.         label = Tkinter.Label(_.frame, text = text, relief = relief, fg = '#000000')
  5785.         label.pack(side = side, fill = fill, padx = padx, expand = expand)
  5786.         setattr(_, name + '_label', label)
  5787.         _._widgets.append(label)
  5788.         if tooltip:
  5789.             b = MfxTooltip(label)
  5790.             _._tooltips.append(b)
  5791.             b.setText(tooltip)
  5792.         
  5793.         return label
  5794.  
  5795.     
  5796.     def updateText(_, **kw):
  5797.         for k, v in kw.items():
  5798.             label = getattr(_, k + '_label')
  5799.             label['text'] = str(v)
  5800.         
  5801.  
  5802.     
  5803.     def configLabel(_, name, **kw):
  5804.         label = getattr(_, name + '_label')
  5805.         apply(label.config, (), kw)
  5806.  
  5807.     
  5808.     def show(_, side = Tkinter.BOTTOM, resize = 0):
  5809.         if side in (0, '', 'none', 'None'):
  5810.             side = None
  5811.         elif not (side in (None, Tkinter.BOTTOM)):
  5812.             side = Tkinter.BOTTOM
  5813.         
  5814.         if _.side == side:
  5815.             return 0
  5816.         
  5817.         if resize:
  5818.             _.top.wm_geometry('')
  5819.         
  5820.         if not side:
  5821.             _.canvas.pack_propagate(0)
  5822.             _.canvas.config(height = 0)
  5823.         else:
  5824.             _.canvas.pack_propagate(1)
  5825.             _.canvas.pack(side = side, fill = Tkinter.X)
  5826.         _.side = side
  5827.         return 1
  5828.  
  5829.     
  5830.     def hide(_, resize = 0):
  5831.         _.show(None, resize)
  5832.  
  5833.     
  5834.     def getSide(_):
  5835.         return _.side
  5836.  
  5837.     
  5838.     def destroy(_):
  5839.         for w in _._tooltips:
  5840.             pass
  5841.         
  5842.         _._tooltips = []
  5843.         for w in _._widgets:
  5844.             pass
  5845.         
  5846.         _._widgets = []
  5847.  
  5848.  
  5849.  
  5850. class PysolStatusbar(_MfxStatusbar):
  5851.     
  5852.     def __init__(_, top):
  5853.         _MfxStatusbar.__init__(_, top)
  5854.         _._createLabel('moves', tooltip = 'Number of moves \nin this game')
  5855.         _._createLabel('gamenumber', tooltip = 'Game number')
  5856.         _._createLabel('stats', tooltip = 'Games played: won/lost')
  5857.         l = _._createLabel('info', fill = Tkinter.X, expand = 0)
  5858.         l.config(text = '', justify = 'left')
  5859.         _._widgets[0].grid_configure(column = 0, row = 0, sticky = 'ew')
  5860.         _._widgets[1].grid_configure(column = 1, row = 0, sticky = 'ew')
  5861.         _._widgets[2].grid_configure(column = 2, row = 0, sticky = 'ew')
  5862.         _._widgets[3].grid_configure(column = 3, row = 0, sticky = 'ew')
  5863.         _.frame.grid_columnconfigure(0, weight = 0, minsize = 100)
  5864.         _.frame.grid_columnconfigure(1, weight = 0, minsize = 200)
  5865.         _.frame.grid_columnconfigure(2, weight = 0, minsize = 100)
  5866.         _.frame.grid_columnconfigure(3, weight = 1)
  5867.  
  5868.  
  5869.  
  5870. class PysolProgressBar:
  5871.     
  5872.     def __init__(_, app, parent, title = None, images = None, color = 'blue', bg = '#c0c0c0', width = 300, height = 25, show_text = 1):
  5873.         _.parent = parent
  5874.         _.percent = 0
  5875.         _.top = makeToplevel(parent, title = title)
  5876.         _.top.wm_protocol('WM_DELETE_WINDOW', _.wmDeleteWindow)
  5877.         _.top.wm_group(parent)
  5878.         _.top.wm_resizable(0, 0)
  5879.         _.frame = Tkinter.Frame(_.top, relief = Tkinter.FLAT, bd = 0, bg = bg, takefocus = 0)
  5880.         _.cframe = Tkinter.Frame(_.frame, relief = Tkinter.SUNKEN, bd = 1, bg = bg, takefocus = 0)
  5881.         _.canvas = Tkinter.Canvas(_.cframe, width = width, height = height, bg = bg, takefocus = 0, bd = 0, highlightthickness = 0)
  5882.         _.scale = _.canvas.create_rectangle(-10, -10, 0, height, outline = color, fill = color)
  5883.         _.text = -1
  5884.         if show_text:
  5885.             _.text = _.canvas.create_text(0, 0, anchor = Tkinter.CENTER)
  5886.         
  5887.         _.cframe.grid_configure(column = 0, row = 0, sticky = 'ew')
  5888.         if images:
  5889.             _.f1 = Tkinter.Label(_.frame, image = images[0], bg = bg)
  5890.             _.f1.grid_configure(column = 0, row = 0, sticky = 'ew', ipadx = 8, ipady = 4)
  5891.             _.cframe.grid_configure(column = 1, row = 0, sticky = 'ew', padx = 8)
  5892.             _.f2 = Tkinter.Label(_.frame, image = images[1], bg = bg)
  5893.             _.f2.grid_configure(column = 2, row = 0, sticky = 'ew', ipadx = 8, ipady = 4)
  5894.         
  5895.         _.top.config(cursor = 'watch')
  5896.         if app:
  5897.             
  5898.             try:
  5899.                 wm_set_icon(_.top, app.dataloader.findIcon())
  5900.             except:
  5901.                 pass
  5902.  
  5903.         
  5904.         _.pack()
  5905.         if 1:
  5906.             setTransient(_.top, None, relx = 0.5, rely = 0.5)
  5907.         else:
  5908.             _.update(percent = 0)
  5909.  
  5910.     
  5911.     def wmDeleteWindow(_):
  5912.         return EVENT_HANDLED
  5913.  
  5914.     
  5915.     def destroy(_):
  5916.         if _.top is None:
  5917.             return None
  5918.         
  5919.         _.top.wm_withdraw()
  5920.         _.top.quit()
  5921.         _.top.destroy()
  5922.         _.top = None
  5923.  
  5924.     
  5925.     def pack(_, **kw):
  5926.         _.canvas.pack(fill = Tkinter.X, expand = 0)
  5927.         apply(_.frame.pack, (), kw)
  5928.  
  5929.     
  5930.     def reset(_, percent = 0):
  5931.         _.percent = percent
  5932.  
  5933.     
  5934.     def update(_, percent = None, step = 1):
  5935.         if _.top is None:
  5936.             return None
  5937.         
  5938.         if percent is None:
  5939.             _.percent = _.percent + step
  5940.         elif percent > _.percent:
  5941.             _.percent = percent
  5942.         else:
  5943.             return None
  5944.         _.percent = min(100, max(0, _.percent))
  5945.         c = _.canvas
  5946.         (width, height) = (c.winfo_reqwidth(), c.winfo_reqheight())
  5947.         c.coords(_.scale, -10, -10, _.percent * width / 100.0, height)
  5948.         if _.text >= 0:
  5949.             c.coords(_.text, width / 2, height / 2)
  5950.             c.itemconfig(_.text, text = '%d %%' % int(round(_.percent)))
  5951.         
  5952.         c.update()
  5953.  
  5954.  
  5955.  
  5956. class MfxMenubar(Tkinter.Menu):
  5957.     addPath = None
  5958.     
  5959.     def __init__(_, master, **kw):
  5960.         if not __debug__ and kw.get('name'):
  5961.             raise AssertionError
  5962.         _.n = kw['tearoff'] = int(kw.get('tearoff', 0))
  5963.         apply(Tkinter.Menu.__init__, (_, master), kw)
  5964.  
  5965.     
  5966.     def labeltoname(_, label):
  5967.         underline = -1
  5968.         m = re.search('^(.*)\\&([^\\&].*)$', label)
  5969.         if m:
  5970.             (l1, l2) = (m.group(1), m.group(2))
  5971.             l1 = re.sub('\\&\\&', '&', l1)
  5972.             l2 = re.sub('\\&\\&', '&', l2)
  5973.             label = l1 + l2
  5974.             underline = len(l1)
  5975.         
  5976.         name = string.lower(re.sub('[^0-9a-zA-Z]', '', label))
  5977.         return (name, label, underline)
  5978.  
  5979.     
  5980.     def add(_, itemType, cnf = { }):
  5981.         label = cnf.get('label')
  5982.         if label:
  5983.             (name, label, underline) = _.labeltoname(label)
  5984.             cnf['label'] = label
  5985.             cnf['underline'] = cnf.get('underline', underline)
  5986.             path = str(_._w) + '.' + name
  5987.             if _.addPath:
  5988.                 _.addPath(path, _, _.n, cnf.get('menu'))
  5989.             
  5990.         
  5991.         Tkinter.Menu.add(_, itemType, cnf)
  5992.         _.n = _.n + 1
  5993.  
  5994.  
  5995.  
  5996. class MfxMenu(MfxMenubar):
  5997.     
  5998.     def __init__(_, master, label, underline = None, **kw):
  5999.         (name, label, label_underline) = _.labeltoname(label)
  6000.         kwdefault(kw, name = name)
  6001.         apply(MfxMenubar.__init__, (_, master), kw)
  6002.         if underline is None:
  6003.             underline = label_underline
  6004.         
  6005.         master.add_cascade(menu = _, label = label, underline = underline)
  6006.  
  6007.  
  6008.  
  6009. class PysolMenubar(PysolMenubarActions):
  6010.     
  6011.     def __init__(_, app, top):
  6012.         PysolMenubarActions.__init__(_, app, top)
  6013.         sh = _.top.winfo_screenheight()
  6014.         _._PysolMenubar__cb_max = 25
  6015.         if sh >= 600:
  6016.             _._PysolMenubar__cb_max = 30
  6017.         
  6018.         if sh >= 768:
  6019.             _._PysolMenubar__cb_max = 35
  6020.         
  6021.         _._PysolMenubar__menubar = None
  6022.         _._PysolMenubar__menupath = { }
  6023.         _._PysolMenubar__keybindings = { }
  6024.         _._createMenubar()
  6025.         _.updateBackgroundImagesMenu()
  6026.         _.top.config(menu = _._PysolMenubar__menubar)
  6027.  
  6028.     
  6029.     def _addPath(_, path, menu, index, submenu):
  6030.         if not _._PysolMenubar__menupath.has_key(path):
  6031.             _._PysolMenubar__menupath[path] = (menu, index, submenu)
  6032.         
  6033.  
  6034.     
  6035.     def _getEnabledState(_, enabled):
  6036.         if enabled:
  6037.             return 'normal'
  6038.         
  6039.         return 'disabled'
  6040.  
  6041.     
  6042.     def _createMenubar(_):
  6043.         MfxMenubar.addPath = _._addPath
  6044.         _._PysolMenubar__menubar = MfxMenubar(_.top, name = 'menubar')
  6045.         bind(_.top, '<KeyPress>', _._keyPressHandler)
  6046.         m = 'Ctrl-'
  6047.         if os.name == 'mac':
  6048.             m = 'Cmd-'
  6049.         
  6050.         menu = _._createMenu('&File')
  6051.         menu.add_command(label = '&New game', command = _.mNewGame, accelerator = 'N')
  6052.         submenu = MfxMenu(menu, label = 'R&ecent games')
  6053.         menu.add_command(label = 'Select &random game', command = _.mSelectRandomGame, accelerator = m + 'R')
  6054.         menu.add_command(label = 'Select game by nu&mber...', command = _.mSelectGameById, accelerator = m + 'M')
  6055.         menu.add_separator()
  6056.         menu.add_command(label = '&Open...', command = _.mOpen, accelerator = m + 'O')
  6057.         menu.add_command(label = '&Save', command = _.mSave, accelerator = m + 'S')
  6058.         menu.add_command(label = 'Save &as...', command = _.mSaveAs)
  6059.         menu.add_separator()
  6060.         menu.add_command(label = '&Hold and quit', command = _.mHoldAndQuit)
  6061.         menu.add_command(label = '&Quit', command = _.mQuit, accelerator = m + 'Q')
  6062.         menu = _._createMenu('&Select')
  6063.         _._addSelectGameMenu(menu)
  6064.         menu = _._createMenu('&Edit')
  6065.         menu.add_command(label = '&Undo', command = _.mUndo, accelerator = 'Z')
  6066.         menu.add_command(label = '&Redo', command = _.mRedo, accelerator = 'R')
  6067.         menu.add_command(label = 'Redo &all', command = _.mRedoAll)
  6068.         menu.add_separator()
  6069.         submenu = MfxMenu(menu, label = '&Set bookmark')
  6070.         for i in range(9):
  6071.             label = 'Bookmark %d' % (i + 1)
  6072.             submenu.add_command(label = label, command = (lambda _ = _, i = i: _.mSetBookmark(i)))
  6073.         
  6074.         submenu = MfxMenu(menu, label = 'Go&to bookmark')
  6075.         for i in range(9):
  6076.             label = 'Bookmark %d' % (i + 1)
  6077.             acc = m + '%d' % (i + 1)
  6078.             submenu.add_command(label = label, command = (lambda _ = _, i = i: _.mGotoBookmark(i)), accelerator = acc)
  6079.         
  6080.         menu.add_command(label = '&Clear bookmarks', command = _.mClearBookmarks)
  6081.         menu.add_separator()
  6082.         menu.add_command(label = 'Restart &game', command = _.mRestart, accelerator = m + 'G')
  6083.         menu = _._createMenu('&Game')
  6084.         menu.add_command(label = 'S&tatus...', command = _.mStatus, accelerator = 'T')
  6085.         menu.add_checkbutton(label = '&Comments...', variable = _.tkopt.comment, command = _.mEditGameComment)
  6086.         menu.add_separator()
  6087.         submenu = MfxMenu(menu, label = '&Statistics')
  6088.         submenu.add_command(label = 'Current game...', command = (lambda _ = _: _.mPlayerStats(mode = 101)))
  6089.         submenu.add_command(label = 'All games...', command = (lambda _ = _: _.mPlayerStats(mode = 102)))
  6090.         submenu.add_separator()
  6091.         submenu.add_command(label = 'Session log...', command = (lambda _ = _: _.mPlayerStats(mode = 104)))
  6092.         submenu.add_command(label = 'Full log...', command = (lambda _ = _: _.mPlayerStats(mode = 103)))
  6093.         submenu = MfxMenu(menu, label = 'Demo statistics')
  6094.         submenu.add_command(label = 'Current game...', command = (lambda _ = _: _.mPlayerStats(mode = 1101)))
  6095.         submenu.add_command(label = 'All games...', command = (lambda _ = _: _.mPlayerStats(mode = 1102)))
  6096.         menu = _._createMenu('&Assist')
  6097.         menu.add_command(label = '&Hint', command = _.mHint, accelerator = 'H')
  6098.         menu.add_separator()
  6099.         menu.add_command(label = '&Demo', command = _.mDemo, accelerator = m + 'D')
  6100.         menu.add_command(label = 'Demo (&all games)', command = _.mMixedDemo)
  6101.         menu = _._createMenu('&Options')
  6102.         menu.add_command(label = '&Player options...', command = _.mOptPlayerOptions)
  6103.         if PACKAGE == 'PyJongg':
  6104.             submenu = MfxMenu(menu, label = 'Assist &level')
  6105.             submenu.add_checkbutton(label = 'Enable &undo', variable = _.tkopt.undo, command = _.mOptEnableUndo)
  6106.             submenu.add_checkbutton(label = 'Enable &bookmarks', variable = _.tkopt.bookmarks, command = _.mOptEnableBookmarks)
  6107.             submenu.add_checkbutton(label = 'Enable &hint', variable = _.tkopt.hint, command = _.mOptEnableHint)
  6108.             submenu.add_checkbutton(label = 'Enable highlight t&iles', variable = _.tkopt.highlight_piles, command = _.mOptEnableHighlightPiles)
  6109.         else:
  6110.             submenu = MfxMenu(menu, label = '&Automatic play')
  6111.             submenu.add_checkbutton(label = 'Auto &face up', variable = _.tkopt.autofaceup, command = _.mOptAutoFaceUp)
  6112.             submenu.add_checkbutton(label = '&Auto drop', variable = _.tkopt.autodrop, command = _.mOptAutoDrop)
  6113.             submenu.add_checkbutton(label = 'Auto &deal', variable = _.tkopt.autodeal, command = _.mOptAutoDeal)
  6114.             submenu.add_separator()
  6115.             submenu.add_checkbutton(label = '&Quick play', variable = _.tkopt.quickplay, command = _.mOptQuickPlay)
  6116.             submenu = MfxMenu(menu, label = 'Assist &level')
  6117.             submenu.add_checkbutton(label = 'Enable &undo', variable = _.tkopt.undo, command = _.mOptEnableUndo)
  6118.             submenu.add_checkbutton(label = 'Enable &bookmarks', variable = _.tkopt.bookmarks, command = _.mOptEnableBookmarks)
  6119.             submenu.add_checkbutton(label = 'Enable &hint', variable = _.tkopt.hint, command = _.mOptEnableHint)
  6120.             submenu.add_checkbutton(label = 'Enable highlight p&iles', variable = _.tkopt.highlight_piles, command = _.mOptEnableHighlightPiles)
  6121.             submenu.add_checkbutton(label = 'Enable highlight &cards', variable = _.tkopt.highlight_cards, command = _.mOptEnableHighlightCards)
  6122.             submenu.add_checkbutton(label = 'Enable highlight same &rank', variable = _.tkopt.highlight_samerank, command = _.mOptEnableHighlightSameRank)
  6123.         menu.add_separator()
  6124.         if _.app.audio.audiodev is None:
  6125.             menu.add_checkbutton(label = '&Sound', variable = _.tkopt.sound, command = _.mOptSound, state = Tkinter.DISABLED)
  6126.         elif pysolsoundserver:
  6127.             menu.add_checkbutton(label = '&Sound...', variable = _.tkopt.sound, command = _.mOptSoundDialog)
  6128.         else:
  6129.             menu.add_checkbutton(label = '&Sound', variable = _.tkopt.sound, command = _.mOptSound)
  6130.         manager = _.app.cardset_manager
  6131.         n = manager.len()
  6132.         if PACKAGE == 'PyJongg':
  6133.             menu.add_command(label = 'Tiles&et...', command = _.mSelectCardsetDialog, accelerator = 'E')
  6134.             submenu = MfxMenu(menu, label = 'Tile &border')
  6135.             menu.add_command(label = 'Table b&ackground...', command = _.mSelectTileDialog)
  6136.         else:
  6137.             menu.add_command(label = 'Cards&et...', command = _.mSelectCardsetDialog, accelerator = 'E')
  6138.             submenu = MfxMenu(menu, label = 'Card &background')
  6139.             menu.add_command(label = 'Table t&ile...', command = _.mSelectTileDialog)
  6140.         submenu = MfxMenu(menu, label = 'A&nimations')
  6141.         submenu.add_radiobutton(label = '&None', variable = _.tkopt.animations, value = 0, command = _.mOptAnimations)
  6142.         submenu.add_radiobutton(label = '&Timer based', variable = _.tkopt.animations, value = 2, command = _.mOptAnimations)
  6143.         submenu.add_radiobutton(label = '&Fast', variable = _.tkopt.animations, value = 1, command = _.mOptAnimations)
  6144.         submenu.add_radiobutton(label = '&Slow', variable = _.tkopt.animations, value = 3, command = _.mOptAnimations)
  6145.         submenu.add_radiobutton(label = '&Very slow', variable = _.tkopt.animations, value = 4, command = _.mOptAnimations)
  6146.         if PACKAGE != 'PyJongg':
  6147.             menu.add_checkbutton(label = 'Card shado&w', variable = _.tkopt.shadow, command = _.mOptShadow)
  6148.             menu.add_checkbutton(label = 'Shade &legal moves', variable = _.tkopt.shade, command = _.mOptShade)
  6149.         
  6150.         menu.add_separator()
  6151.         menu.add_command(label = '&Hint options...', command = _.mOptHintOptions)
  6152.         menu.add_command(label = '&Demo options...', command = _.mOptDemoOptions)
  6153.         menu.add_separator()
  6154.         submenu = MfxMenu(menu, label = '&Toolbar')
  6155.         submenu.add_radiobutton(label = '&Hide', variable = _.tkopt.toolbar, value = 0, command = _.mOptToolbar)
  6156.         submenu.add_radiobutton(label = '&Top', variable = _.tkopt.toolbar, value = 1, command = _.mOptToolbar)
  6157.         submenu.add_radiobutton(label = '&Bottom', variable = _.tkopt.toolbar, value = 2, command = _.mOptToolbar)
  6158.         submenu.add_separator()
  6159.         submenu.add_radiobutton(label = '&Small icons', variable = _.tkopt.toolbar_size, value = 0, command = _.mOptToolbarSize)
  6160.         submenu.add_radiobutton(label = '&Large icons', variable = _.tkopt.toolbar_size, value = 1, command = _.mOptToolbarSize)
  6161.         menu.add_checkbutton(label = 'Stat&usbar', variable = _.tkopt.statusbar, command = _.mOptStatusbar)
  6162.         menu = _._createMenu('&Help')
  6163.         menu.add_command(label = '&Contents', command = _.mHelp, accelerator = m + 'F1')
  6164.         menu.add_command(label = '&How to play', command = _.mHelpHowToPlay)
  6165.         menu.add_command(label = '&Rules for this game', command = _.mHelpRules, accelerator = 'F1')
  6166.         if bundle & 4 == 0:
  6167.             menu.add_command(label = '&License terms', command = _.mHelpLicense)
  6168.             menu.add_command(label = "What's &new ?", command = _.mHelpNews)
  6169.             if os.name == 'nt':
  6170.                 menu.add_separator()
  6171.                 menu.add_command(label = PACKAGE + ' &Web Site', command = _.mHelpWebSite)
  6172.             
  6173.         
  6174.         menu.add_separator()
  6175.         menu.add_command(label = '&About ' + PACKAGE + '...', command = _.mHelpAbout)
  6176.         if os.name == 'mac':
  6177.             menu = _._createMenu('Apple')
  6178.             menu.add_command(label = 'About ' + PACKAGE + '...', command = _.mHelpAbout)
  6179.             menu.add_command(label = PACKAGE + ' help', command = _.mHelp)
  6180.         
  6181.         MfxMenubar.addPath = None
  6182.         ctrl = 'Control-'
  6183.         if os.name == 'mac':
  6184.             ctrl = 'Command-'
  6185.         
  6186.         _._bindKey('', 'n', _.mNewGame)
  6187.         _._bindKey('', 'g', _.mSelectGameDialog)
  6188.         _._bindKey('', 'v', _.mSelectGameDialogWithPreview)
  6189.         _._bindKey(ctrl, 'r', _.mSelectRandomGame)
  6190.         _._bindKey(ctrl, 'm', _.mSelectGameById)
  6191.         _._bindKey(ctrl, 'n', _.mNewGameWithNextId)
  6192.         _._bindKey(ctrl, 'o', _.mOpen)
  6193.         _._bindKey(ctrl, 's', _.mSave)
  6194.         _._bindKey(ctrl, 'q', _.mQuit)
  6195.         _._bindKey('', 'z', _.mUndo)
  6196.         _._bindKey('', 'BackSpace', _.mUndo)
  6197.         _._bindKey('', 'r', _.mRedo)
  6198.         _._bindKey(ctrl, 'g', _.mRestart)
  6199.         _._bindKey('', 'space', _.mDeal)
  6200.         _._bindKey('', 't', _.mStatus)
  6201.         _._bindKey(ctrl, 't', _.mPlayerStats)
  6202.         _._bindKey('', 'h', _.mHint)
  6203.         _._bindKey(ctrl, 'h', _.mHint1)
  6204.         _._bindKey('', 'i', _.mHighlightPiles)
  6205.         _._bindKey(ctrl, 'd', _.mDemo)
  6206.         _._bindKey('', 'e', _.mSelectCardsetDialog)
  6207.         _._bindKey(ctrl, 'b', _.mOptChangeCardback)
  6208.         _._bindKey(ctrl, 'i', _.mOptChangeTableTile)
  6209.         _._bindKey(ctrl, 'p', _.mOptPlayerOptions)
  6210.         _._bindKey(ctrl, 'F1', _.mHelp)
  6211.         _._bindKey('', 'F1', _.mHelpRules)
  6212.         _._bindKey('', 'Print', _.mScreenshot)
  6213.         _._bindKey(ctrl, 'u', _.mPlayNextMusic)
  6214.         _._bindKey('', 'a', _.mDrop)
  6215.         _._bindKey(ctrl, 'a', _.mDrop1)
  6216.         _._bindKey('', 's', _.mUndo)
  6217.         _._bindKey('', 'd', _.mDeal)
  6218.         _._bindKey('', 'l', _.mDrop)
  6219.         _._bindKey(ctrl, 'l', _.mDrop1)
  6220.         _._bindKey('', 'k', _.mUndo)
  6221.         _._bindKey('', 'j', _.mDeal)
  6222.         for i in range(9):
  6223.             _._bindKey(ctrl, str(i + 1), (lambda event, _ = _, i = i: _.mGotoBookmark(i, confirm = 0)))
  6224.         
  6225.  
  6226.     
  6227.     def _bindKey(_, modifier, key, func):
  6228.         if 0 and not modifier and len(key) == 1:
  6229.             _._PysolMenubar__keybindings[string.lower(key)] = func
  6230.             _._PysolMenubar__keybindings[string.upper(key)] = func
  6231.             return None
  6232.         
  6233.         sequence = '<' + modifier + 'KeyPress-' + key + '>'
  6234.         
  6235.         try:
  6236.             bind(_.top, sequence, func)
  6237.             if len(key) == 1 and key != string.upper(key):
  6238.                 key = string.upper(key)
  6239.                 sequence = '<' + modifier + 'KeyPress-' + key + '>'
  6240.                 bind(_.top, sequence, func)
  6241.         except:
  6242.             pass
  6243.  
  6244.  
  6245.     
  6246.     def _keyPressHandler(_, event):
  6247.         r = EVENT_PROPAGATE
  6248.         if event and _.game:
  6249.             if _.game.demo:
  6250.                 if event.char:
  6251.                     _.game.demo.keypress = event.char
  6252.                     r = EVENT_HANDLED
  6253.                 
  6254.             
  6255.             func = _._PysolMenubar__keybindings.get(event.char)
  6256.             if func and event.state & ~2 == 0:
  6257.                 func(event)
  6258.                 r = EVENT_HANDLED
  6259.             
  6260.         
  6261.         return r
  6262.  
  6263.     
  6264.     def _createMenu(_, label, tearoff = 0):
  6265.         if os.name == 'posix':
  6266.             tearoff = 1
  6267.         
  6268.         menu = MfxMenu(_._PysolMenubar__menubar, label, tearoff = tearoff, underline = -1)
  6269.         return menu
  6270.  
  6271.     
  6272.     def _addSelectGameMenu(_, menu):
  6273.         games = map(_.app.gdb.get, _.app.gdb.getGamesIdSortedByShortName())
  6274.         games = tuple(games)
  6275.         menu.add_command(label = 'All &games...', command = _.mSelectGameDialog, accelerator = 'G')
  6276.         menu.add_command(label = 'Playable pre&view...', command = _.mSelectGameDialogWithPreview, accelerator = 'V')
  6277.         menu.add_separator()
  6278.         data = ('&Popular games', (lambda gi: gi.si.game_flags & GI.GT_POPULAR))
  6279.         _._addSelectGameSubMenu(menu, games, (data,), _.mSelectGamePopular, _.tkopt.gameid_popular)
  6280.         if PACKAGE == 'PyJongg':
  6281.             menu.add_separator()
  6282.             _._addSelectMahjonggGameSubMenu(menu, games, _.mSelectGame, _.tkopt.gameid)
  6283.             return None
  6284.         
  6285.         submenu = MfxMenu(menu, label = '&Special games')
  6286.         _._addSelectGameSubMenu(submenu, games, GI.SELECT_SPECIAL_GAME_BY_TYPE, _.mSelectGame, _.tkopt.gameid)
  6287.         menu.add_separator()
  6288.         _._addSelectGameSubMenu(menu, games, GI.SELECT_GAME_BY_TYPE, _.mSelectGame, _.tkopt.gameid)
  6289.  
  6290.     
  6291.     def _addSelectGameSubMenu(_, menu, games, select_data, command, variable):
  6292.         need_sep = 0
  6293.         for label, select_func in select_data:
  6294.             g = filter(select_func, games)
  6295.             if not g:
  6296.                 continue
  6297.             
  6298.             if need_sep:
  6299.                 menu.add_separator()
  6300.                 need_sep = 0
  6301.             
  6302.             submenu = MfxMenu(menu, label = label)
  6303.             if 1 and label == 'Mahjongg type':
  6304.                 pass
  6305.             else:
  6306.                 _._addSelectGameSubSubMenu(submenu, g, command, variable)
  6307.         
  6308.  
  6309.     
  6310.     def _addSelectMahjonggGameSubMenu(_, menu, g, command, variable):
  6311.         for c in ('AC', 'DF', 'GK', 'LR', 'ST', 'UZ'):
  6312.             gg = filter((lambda gi, c0 = c[0], c1 = c[1]: None if gi.short_name[0] <= gi.short_name[0] else gi.short_name[0] <= c1), g)
  6313.             label = c[0]
  6314.             if c[0] != c[1]:
  6315.                 label = c[0] + ' - ' + c[1]
  6316.             
  6317.             submenu = MfxMenu(menu, label = label)
  6318.             _._addSelectGameSubSubMenu(submenu, gg, command, variable)
  6319.         
  6320.  
  6321.     
  6322.     def _addSelectGameSubSubMenu(_, menu, g, command, variable):
  6323.         cb = (25, _._PysolMenubar__cb_max)[len(g) > 4 * 25]
  6324.         for i in range(len(g)):
  6325.             gi = g[i]
  6326.             if i > 0:
  6327.                 pass
  6328.             columnbreak = i % cb == 0
  6329.             menu.add_radiobutton(command = command, variable = variable, columnbreak = columnbreak, value = gi.id, label = gi.short_name)
  6330.         
  6331.  
  6332.     
  6333.     def _mSelectGameDialog(_, d):
  6334.         if d.status == 0 and d.button == 0 and d.gameid != _.game.id:
  6335.             _.tkopt.gameid.set(d.gameid)
  6336.             _.tkopt.gameid_popular.set(d.gameid)
  6337.             _.game.endGame()
  6338.             _.game.quitGame(d.gameid, random = d.random)
  6339.         
  6340.         return EVENT_HANDLED
  6341.  
  6342.     
  6343.     def __restoreCursor(_, *event):
  6344.         _.game.setCursor(cursor = _.app.top_cursor)
  6345.  
  6346.     
  6347.     def mSelectGameDialog(_, *event):
  6348.         if _._cancelDrag():
  6349.             return None
  6350.         
  6351.         _.game.setCursor(cursor = CURSOR_WATCH)
  6352.         after_idle(_.top, _._PysolMenubar__restoreCursor)
  6353.         d = SelectGameDialog(_.top, title = 'Select game', app = _.app, gameid = _.game.id)
  6354.         return _._mSelectGameDialog(d)
  6355.  
  6356.     
  6357.     def mSelectGameDialogWithPreview(_, *event):
  6358.         if _._cancelDrag():
  6359.             return None
  6360.         
  6361.         _.game.setCursor(cursor = CURSOR_WATCH)
  6362.         bookmark = None
  6363.         after_idle(_.top, _._PysolMenubar__restoreCursor)
  6364.         d = SelectGameDialogWithPreview(_.top, title = 'Select game', app = _.app, gameid = _.game.id, bookmark = bookmark)
  6365.         return _._mSelectGameDialog(d)
  6366.  
  6367.     
  6368.     def updateRecentGamesMenu(_, gameids):
  6369.         submenu = _._PysolMenubar__menupath['.menubar.file.recentgames'][2]
  6370.         submenu.delete(0, 'last')
  6371.         cb = (25, _._PysolMenubar__cb_max)[len(gameids) > 4 * 25]
  6372.         i = 0
  6373.         for id in gameids:
  6374.             gi = _.app.getGameInfo(id)
  6375.             if i > 0:
  6376.                 pass
  6377.             columnbreak = i % cb == 0
  6378.             submenu.add_radiobutton(command = _.mSelectGame, variable = _.tkopt.gameid, columnbreak = columnbreak, value = gi.id, label = gi.short_name)
  6379.             i = i + 1
  6380.         
  6381.  
  6382.     
  6383.     def updateBookmarkMenuState(_):
  6384.         state = _._getEnabledState
  6385.         mp1 = _._PysolMenubar__menupath.get('.menubar.edit.setbookmark')
  6386.         mp2 = _._PysolMenubar__menupath.get('.menubar.edit.gotobookmark')
  6387.         mp3 = _._PysolMenubar__menupath.get('.menubar.edit.clearbookmarks')
  6388.         if mp1 is None and mp2 is None or mp3 is None:
  6389.             return None
  6390.         
  6391.         if _.app.opt.bookmarks:
  6392.             pass
  6393.         x = _.game.canSetBookmark()
  6394.         (menu, index, submenu) = mp1
  6395.         for i in range(9):
  6396.             submenu.entryconfig(i, state = state(x))
  6397.         
  6398.         menu.entryconfig(index, state = state(x))
  6399.         (menu, index, submenu) = mp2
  6400.         ms = 0
  6401.         for i in range(9):
  6402.             s = _.game.gsaveinfo.bookmarks.get(i) is not None
  6403.             if s:
  6404.                 pass
  6405.             submenu.entryconfig(i, state = state(x))
  6406.             if not ms:
  6407.                 pass
  6408.             ms = s
  6409.         
  6410.         if ms:
  6411.             pass
  6412.         menu.entryconfig(index, state = state(x))
  6413.         (menu, index, submenu) = mp3
  6414.         if ms:
  6415.             pass
  6416.         menu.entryconfig(index, state = state(x))
  6417.  
  6418.     
  6419.     def updateBackgroundImagesMenu(_):
  6420.         mp = _._PysolMenubar__menupath.get('.menubar.options.cardbackground')
  6421.         if PACKAGE == 'PyJongg' and mp is None:
  6422.             mp = _._PysolMenubar__menupath.get('.menubar.options.tileborder')
  6423.         
  6424.         submenu = mp[2]
  6425.         submenu.delete(0, 'last')
  6426.         mbacks = _.app.images.getCardbacks()
  6427.         cb = int(math.ceil(math.sqrt(len(mbacks))))
  6428.         for i in range(len(mbacks)):
  6429.             if i > 0:
  6430.                 pass
  6431.             columnbreak = i % cb == 0
  6432.             submenu.add_radiobutton(label = mbacks[i].name, image = mbacks[i].menu_image, variable = _.tkopt.cardback, value = i, command = _.mOptCardback, columnbreak = columnbreak, indicatoron = 0, hidemargin = 0)
  6433.         
  6434.  
  6435.     
  6436.     def setMenuState(_, state, path):
  6437.         path = '.menubar.' + path
  6438.         mp = _._PysolMenubar__menupath.get(path)
  6439.         if PACKAGE == 'PyJongg' and mp is None:
  6440.             return None
  6441.         
  6442.         (menu, index, submenu) = mp
  6443.         s = _._getEnabledState(state)
  6444.         menu.entryconfig(index, state = s)
  6445.  
  6446.     
  6447.     def setToolbarState(_, state, path):
  6448.         s = _._getEnabledState(state)
  6449.         w = getattr(_.app.toolbar, path + '_button')
  6450.         w['state'] = s
  6451.  
  6452.     if PACKAGE == 'PyJongg':
  6453.         DEFAULTEXTENSION = '.pjo'
  6454.     else:
  6455.         DEFAULTEXTENSION = '.pso'
  6456.     FILETYPES = ((PACKAGE + ' files', '*' + DEFAULTEXTENSION), ('All files', '*'))
  6457.     
  6458.     def mOpen(_, *event):
  6459.         if _._cancelDrag():
  6460.             return None
  6461.         
  6462.         filename = _.game.filename
  6463.         if filename:
  6464.             (idir, ifile) = os.path.split(os.path.normpath(filename))
  6465.         else:
  6466.             (idir, ifile) = ('', '')
  6467.         if not idir:
  6468.             idir = _.app.dn.savegames
  6469.         
  6470.         d = tkFileDialog.Open()
  6471.         filename = d.show(filetypes = _.FILETYPES, defaultextension = _.DEFAULTEXTENSION, initialdir = idir, initialfile = ifile)
  6472.         if filename:
  6473.             filename = os.path.normpath(filename)
  6474.             if os.path.isfile(filename):
  6475.                 _.game.loadGame(filename)
  6476.             
  6477.         
  6478.  
  6479.     
  6480.     def mSaveAs(_, *event):
  6481.         if _._cancelDrag():
  6482.             return None
  6483.         
  6484.         if not (_.menustate.save_as):
  6485.             return None
  6486.         
  6487.         filename = _.game.filename
  6488.         if not filename:
  6489.             filename = _.app.getGameSaveName(_.game.id)
  6490.             if os.name == 'posix':
  6491.                 filename = filename + '-' + _.game.getGameNumber(format = 0)
  6492.             else:
  6493.                 filename = filename + '-01'
  6494.             filename = filename + _.DEFAULTEXTENSION
  6495.         
  6496.         (idir, ifile) = os.path.split(os.path.normpath(filename))
  6497.         if not idir:
  6498.             idir = _.app.dn.savegames
  6499.         
  6500.         d = tkFileDialog.SaveAs()
  6501.         filename = d.show(filetypes = _.FILETYPES, defaultextension = _.DEFAULTEXTENSION, initialdir = idir, initialfile = ifile)
  6502.         if filename:
  6503.             filename = os.path.normpath(filename)
  6504.             _.game.saveGame(filename)
  6505.             _.updateMenus()
  6506.         
  6507.  
  6508.     
  6509.     def mSelectCardsetDialog(_, *event):
  6510.         if _._cancelDrag():
  6511.             return None
  6512.         
  6513.         (strings, default) = ((None, 'Load', 'Cancel'), 1)
  6514.         if PACKAGE == 'PySol':
  6515.             if os.name == 'posix':
  6516.                 (strings, default) = ((None, 'Load', 'Cancel', 'Info...'), 1)
  6517.             
  6518.         
  6519.         t = CARDSET
  6520.         key = _.app.nextgame.cardset.index
  6521.         d = SelectCardsetDialogWithPreview(_.top, title = 'Select ' + t, app = _.app, manager = _.app.cardset_manager, key = key, strings = strings, default = default)
  6522.         cs = _.app.cardset_manager.get(d.key)
  6523.         if cs is None or d.key == _.app.cardset.index:
  6524.             return None
  6525.         
  6526.         if d.status == 0 and d.button in (0, 1) and d.key >= 0:
  6527.             _.app.nextgame.cardset = cs
  6528.             if d.button == 1:
  6529.                 _.game.endGame(bookmark = 1)
  6530.                 _.game.quitGame(bookmark = 1)
  6531.             
  6532.         
  6533.  
  6534.     
  6535.     def _mOptCardback(_, index):
  6536.         if _._cancelDrag():
  6537.             return None
  6538.         
  6539.         cs = _.app.cardset
  6540.         old_index = cs.backindex
  6541.         cs.updateCardback(backindex = index)
  6542.         if cs.backindex == old_index:
  6543.             return None
  6544.         
  6545.         _.app.updateCardset(_.game.id)
  6546.         for card in _.game.cards:
  6547.             image = _.app.images.getBack(card.deck, card.suit, card.rank)
  6548.             card.updateCardBackground(image = image)
  6549.         
  6550.         _.app.canvas.update_idletasks()
  6551.         _.tkopt.cardback.set(cs.backindex)
  6552.  
  6553.     
  6554.     def mOptCardback(_, *event):
  6555.         _._mOptCardback(_.tkopt.cardback.get())
  6556.  
  6557.     
  6558.     def mOptChangeCardback(_, *event):
  6559.         _._mOptCardback(_.app.cardset.backindex + 1)
  6560.  
  6561.     
  6562.     def _mOptTableTile(_, i):
  6563.         if _.app.tabletile_index != i:
  6564.             tile = _.app.tabletile_manager.get(i)
  6565.             if _.game.canvas.setTile(tile.filename):
  6566.                 _.game.canvas.setTextColor(tile.text_color)
  6567.                 if _.app.debug:
  6568.                     _.game.updateStatus(info = tile.basename)
  6569.                 
  6570.                 _.tkopt.tabletile.set(i)
  6571.                 _.app.tabletile_index = i
  6572.                 _.app.opt.tabletile_name = tile.basename
  6573.                 _.app.tabletile_manager.setSelected(i)
  6574.             
  6575.         
  6576.  
  6577.     
  6578.     def mOptTableTile(_, *event):
  6579.         if _._cancelDrag():
  6580.             return None
  6581.         
  6582.         _._mOptTableTile(_.tkopt.tabletile.get())
  6583.  
  6584.     
  6585.     def mOptChangeTableTile(_, *event):
  6586.         if _._cancelDrag():
  6587.             return None
  6588.         
  6589.         n = _.app.tabletile_manager.len()
  6590.         if n >= 2:
  6591.             i = (_.tkopt.tabletile.get() + 1) % n
  6592.             _._mOptTableTile(i)
  6593.         
  6594.  
  6595.     
  6596.     def mSelectTileDialog(_, *event):
  6597.         if _._cancelDrag():
  6598.             return None
  6599.         
  6600.         key = _.app.tabletile_index
  6601.         if key <= 0:
  6602.             key = string.lower(_.app.opt.tablecolor)
  6603.         
  6604.         d = SelectTileDialogWithPreview(_.top, title = 'Select table background', app = _.app, manager = _.app.tabletile_manager, key = key)
  6605.         if d.status == 0 and d.button in (0, 1):
  6606.             if type(d.key) is types.StringType:
  6607.                 _._mOptTableColor(d.key)
  6608.             elif d.key > 0 and d.key != _.app.tabletile_index:
  6609.                 _._mOptTableTile(d.key)
  6610.             
  6611.         
  6612.  
  6613.     
  6614.     def _mOptTableColor(_, color):
  6615.         if _.app.opt.tablecolor != color:
  6616.             _.app.opt.tablecolor = color
  6617.             _.game.canvas.config(bg = color)
  6618.             _.top.config(bg = color)
  6619.         
  6620.         _._mOptTableTile(0)
  6621.         _.game.canvas.setTextColor(None)
  6622.  
  6623.     
  6624.     def mOptTableColor(_, *event):
  6625.         if _._cancelDrag():
  6626.             return None
  6627.         
  6628.         c = tkColorChooser.askcolor(initialcolor = _.app.opt.tablecolor, title = 'Select table color')
  6629.         if c and c[1]:
  6630.             _._mOptTableColor(c[1])
  6631.         
  6632.  
  6633.     
  6634.     def mOptToolbar(_, *event):
  6635.         if _._cancelDrag():
  6636.             return None
  6637.         
  6638.         _.setToolbarSide(_.tkopt.toolbar.get())
  6639.  
  6640.     
  6641.     def mOptToolbarSize(_, *event):
  6642.         if _._cancelDrag():
  6643.             return None
  6644.         
  6645.         _.setToolbarSize(_.tkopt.toolbar_size.get())
  6646.  
  6647.     
  6648.     def mOptToolbarRelief(_, *event):
  6649.         if _._cancelDrag():
  6650.             return None
  6651.         
  6652.         _.setToolbarRelief(_.tkopt.toolbar_relief.get())
  6653.  
  6654.     
  6655.     def mOptStatusbar(_, *event):
  6656.         if _._cancelDrag():
  6657.             return None
  6658.         
  6659.         if not (_.app.statusbar):
  6660.             return None
  6661.         
  6662.         side = _.tkopt.statusbar.get()
  6663.         _.app.opt.statusbar = side
  6664.         if _.app.statusbar.show(side):
  6665.             _.top.update_idletasks()
  6666.         
  6667.  
  6668.     
  6669.     def setToolbarSide(_, side):
  6670.         if _._cancelDrag():
  6671.             return None
  6672.         
  6673.         _.app.opt.toolbar = side
  6674.         _.tkopt.toolbar.set(side)
  6675.         if _.app.toolbar.show(side):
  6676.             _.top.update_idletasks()
  6677.         
  6678.  
  6679.     
  6680.     def setToolbarSize(_, size):
  6681.         if _._cancelDrag():
  6682.             return None
  6683.         
  6684.         _.app.opt.toolbar_size = size
  6685.         _.tkopt.toolbar_size.set(size)
  6686.         dir = _.app.getToolbarImagesDir(size)
  6687.         if _.app.toolbar.updateImages(dir, size):
  6688.             _.game.updateStatus(player = _.app.opt.player)
  6689.             _.top.update_idletasks()
  6690.         
  6691.  
  6692.     
  6693.     def setToolbarRelief(_, relief):
  6694.         if _._cancelDrag():
  6695.             return None
  6696.         
  6697.         _.app.opt.toolbar_relief = relief
  6698.         _.tkopt.toolbar_relief.set(relief)
  6699.         _.top.update_idletasks()
  6700.  
  6701.  
  6702.  
  6703. class MfxTreeBaseNode:
  6704.     
  6705.     def __init__(_, tree, parent_node, text, key):
  6706.         _.tree = tree
  6707.         _.parent_node = parent_node
  6708.         _.text = text
  6709.         _.key = key
  6710.         _.selected = 0
  6711.         _.subnodes = None
  6712.         _.symbol_id = -1
  6713.         _.text_id = -1
  6714.         _.textrect_id = -1
  6715.  
  6716.     
  6717.     def registerKey(_):
  6718.         if _.key is not None:
  6719.             l = _.tree.keys.get(_.key, [])
  6720.             l.append(_)
  6721.             _.tree.keys[_.key] = l
  6722.         
  6723.  
  6724.     
  6725.     def whoami(_):
  6726.         if _.parent_node is None:
  6727.             return (_.text,)
  6728.         else:
  6729.             return _.parent_node.whoami() + (_.text,)
  6730.  
  6731.     
  6732.     def draw(_, x, y, lastx = None, lasty = None):
  6733.         (canvas, style) = (_.tree.canvas, _.tree.style)
  6734.         topleftx = x + style.distx
  6735.         toplefty = y - style.height / 2
  6736.         if lastx is not None:
  6737.             canvas.create_line(x, y, topleftx, y, stipple = style.linestyle, fill = style.linecolor)
  6738.         
  6739.         _.selected = 0
  6740.         _.symbol_id = -1
  6741.         _.drawSymbol(topleftx, toplefty)
  6742.         linestart = style.distx + style.width + 5
  6743.         _.text_id = -1
  6744.         _.drawText(x + linestart, y)
  6745.         return (x, y, x, y + style.disty)
  6746.  
  6747.     
  6748.     def drawText(_, x, y):
  6749.         (canvas, style) = (_.tree.canvas, _.tree.style)
  6750.         if _.selected:
  6751.             (fg, bg) = (style.text_selected_fg, style.text_selected_bg)
  6752.         else:
  6753.             (fg, bg) = (style.text_normal_fg, style.text_normal_bg)
  6754.         if _.tree.nodes.get(_.text_id) is _:
  6755.             canvas.itemconfig(_.text_id, fill = fg)
  6756.         else:
  6757.             _.text_id = canvas.create_text(x + 1, y, text = _.text, anchor = 'w', justify = 'left', font = style.font, fill = fg)
  6758.             _.tree.nodes[_.text_id] = _
  6759.         if _.tree.nodes.get(_.textrect_id) is _:
  6760.             canvas.itemconfig(_.textrect_id, fill = bg)
  6761.         elif _.selected:
  6762.             b = canvas.bbox(_.text_id)
  6763.             _.textrect_id = canvas.create_rectangle(b[0] - 1, b[1] - 1, b[2] + 1, b[3] + 1, fill = bg, outline = '')
  6764.             canvas.tag_lower(_.textrect_id, _.text_id)
  6765.             _.tree.nodes[_.textrect_id] = _
  6766.         
  6767.  
  6768.     
  6769.     def updateText(_):
  6770.         if _.tree.nodes.get(_.text_id) is _:
  6771.             _.drawText(-1, -1)
  6772.         
  6773.  
  6774.     
  6775.     def drawSymbol(_, x, y, **kw):
  6776.         (canvas, style) = (_.tree.canvas, _.tree.style)
  6777.         color = kw.get('color')
  6778.         if color is None:
  6779.             if _.selected:
  6780.                 color = 'darkgreen'
  6781.             else:
  6782.                 color = 'green'
  6783.         
  6784.         if _.tree.nodes.get(_.symbol_id) is _:
  6785.             canvas.itemconfig(_.symbol_id, fill = color)
  6786.         else:
  6787.             _.symbol_id = canvas.create_rectangle(x + 1, y + 1, x + style.width, y + style.height, fill = color)
  6788.             _.tree.nodes[_.symbol_id] = _
  6789.  
  6790.     
  6791.     def updateSymbol(_):
  6792.         if _.tree.nodes.get(_.symbol_id) is _:
  6793.             _.drawSymbol(-1, -1)
  6794.         
  6795.  
  6796.  
  6797.  
  6798. class MfxTreeLeaf(MfxTreeBaseNode):
  6799.     
  6800.     def drawText(_, x, y):
  6801.         if _.text_id < 0:
  6802.             _.registerKey()
  6803.         
  6804.         MfxTreeBaseNode.drawText(_, x, y)
  6805.  
  6806.  
  6807.  
  6808. class MfxTreeNode(MfxTreeBaseNode):
  6809.     
  6810.     def __init__(_, tree, parent_node, text, key, expanded = 0):
  6811.         MfxTreeBaseNode.__init__(_, tree, parent_node, text, key)
  6812.         _.expanded = expanded
  6813.  
  6814.     
  6815.     def drawChildren(_, x, y, lastx, lasty):
  6816.         _.subnodes = _.tree.getContents(_)
  6817.         (lx, ly) = (lastx, lasty)
  6818.         (nx, ny) = (x, y)
  6819.         for node in _.subnodes:
  6820.             node.tree = _.tree
  6821.             (lx, ly, nx, ny) = node.draw(nx, ny, lx, ly)
  6822.         
  6823.         return ny
  6824.  
  6825.     
  6826.     def draw(_, x, y, ilastx = None, ilasty = None):
  6827.         (lx, ly, nx, ny) = MfxTreeBaseNode.draw(_, x, y, ilastx, ilasty)
  6828.         if _.expanded:
  6829.             style = _.tree.style
  6830.             childx = nx + style.distx + style.width / 2
  6831.             childy = ny
  6832.             clastx = nx + style.distx + style.width / 2
  6833.             clasty = ly + style.height / 2
  6834.             ny = _.drawChildren(childx, childy, clastx, clasty)
  6835.         
  6836.         return (lx, ly, x, ny)
  6837.  
  6838.     
  6839.     def drawSymbol(_, x, y, **kw):
  6840.         color = kw.get('color')
  6841.         if color is None:
  6842.             if _.expanded:
  6843.                 color = 'red'
  6844.             else:
  6845.                 color = 'pink'
  6846.         
  6847.         MfxTreeBaseNode.drawSymbol(_, x, y, color = color)
  6848.  
  6849.  
  6850.  
  6851. class MfxTreeInCanvas(MfxScrolledCanvas):
  6852.     
  6853.     class Style:
  6854.         
  6855.         def __init__(_):
  6856.             _.distx = 16
  6857.             _.disty = 18
  6858.             _.width = 16
  6859.             _.height = 16
  6860.             _.originx = 0
  6861.             _.originy = 0
  6862.             _.text_normal_fg = 'black'
  6863.             _.text_normal_bg = 'white'
  6864.             _.text_selected_fg = 'white'
  6865.             _.text_selected_bg = '#00008b'
  6866.             _.font = getFont('tree_small')
  6867.             _.linestyle = 'gray50'
  6868.             _.linecolor = 'black'
  6869.             if os.name == 'nt':
  6870.                 _.linestyle = ''
  6871.                 _.linecolor = 'gray50'
  6872.             
  6873.  
  6874.  
  6875.     
  6876.     def __init__(_, parent, rootnodes, c_width = 400, c_height = 300, **kw):
  6877.         if not kw.get('bg'):
  6878.             pass
  6879.         bg = kw['bg'] = parent.cget('bg')
  6880.         apply(MfxScrolledCanvas.__init__, (_, parent), kw)
  6881.         _.rootnodes = rootnodes
  6882.         _.updateNodesWithTree(_.rootnodes, _)
  6883.         _.selection_key = None
  6884.         _.nodes = { }
  6885.         _.keys = { }
  6886.         _.style = _.Style()
  6887.         _.style.text_normal_fg = _.canvas.cget('insertbackground')
  6888.         _.style.text_normal_bg = bg
  6889.         _.canvas.config(width = c_width, height = c_height)
  6890.         bind(_.canvas, '<ButtonPress-1>', _.singleClick)
  6891.         bind(_.canvas, '<Double-Button-1>', _.doubleClick)
  6892.         _.frame.pack(fill = Tkinter.BOTH, expand = 1)
  6893.  
  6894.     
  6895.     def destroy(_):
  6896.         for node in _.keys.get(_.selection_key, []):
  6897.             node.selected = 0
  6898.         
  6899.         MfxScrolledCanvas.destroy(_)
  6900.  
  6901.     
  6902.     def findNode(_, event = None):
  6903.         id = _.canvas.find_withtag(Tkinter.CURRENT)
  6904.         if id:
  6905.             return _.nodes.get(id[0])
  6906.         
  6907.         return None
  6908.  
  6909.     
  6910.     def draw(_):
  6911.         (nx, ny) = (_.style.originx, _.style.originy)
  6912.         nx = nx - _.style.distx
  6913.         ny = ny + _.style.height / 2
  6914.         for node in _.rootnodes:
  6915.             node.tree = _
  6916.             (lx, ly) = (None, None)
  6917.             (lx, ly, nx, ny) = node.draw(nx, ny, lx, ly)
  6918.         
  6919.         bbox = _.canvas.bbox('all')
  6920.         _.canvas.config(scrollregion = (0, 0, bbox[2], bbox[3]))
  6921.         _.canvas.config(yscrollincrement = _.style.disty)
  6922.         _.showVbar()
  6923.         _.showHbar()
  6924.  
  6925.     
  6926.     def clear(_):
  6927.         _.nodes = { }
  6928.         _.keys = { }
  6929.         _.canvas.delete('all')
  6930.  
  6931.     
  6932.     def redraw(_):
  6933.         oldcur = _.canvas['cursor']
  6934.         _.canvas['cursor'] = 'watch'
  6935.         _.canvas.update_idletasks()
  6936.         _.clear()
  6937.         _.draw()
  6938.         _.updateSelection(_.selection_key)
  6939.         _.showVbar()
  6940.         _.showHbar()
  6941.         _.canvas['cursor'] = oldcur
  6942.  
  6943.     
  6944.     def getContents(_, node):
  6945.         pass
  6946.  
  6947.     
  6948.     def singleClick(_, event = None):
  6949.         pass
  6950.  
  6951.     
  6952.     def doubleClick(_, event = None):
  6953.         _.singleClick(event)
  6954.  
  6955.     
  6956.     def updateSelection(_, key):
  6957.         l1 = _.keys.get(_.selection_key, [])
  6958.         l2 = _.keys.get(key, [])
  6959.         for node in l1:
  6960.             pass
  6961.         
  6962.         for node in l2:
  6963.             pass
  6964.         
  6965.         _.selection_key = key
  6966.  
  6967.     
  6968.     def updateNodesWithTree(_, nodes, tree):
  6969.         for node in nodes:
  6970.             node.tree = tree
  6971.         
  6972.  
  6973.  
  6974.  
  6975. class SelectDialogTreeLeaf(MfxTreeLeaf):
  6976.     
  6977.     def drawSymbol(_, x, y, **kw):
  6978.         if _.tree.nodes.get(_.symbol_id) is not _:
  6979.             _.symbol_id = _.tree.canvas.create_image(x, y, image = _.tree.data.img[2 + (_.key is None)], anchor = 'nw')
  6980.             _.tree.nodes[_.symbol_id] = _
  6981.         
  6982.  
  6983.  
  6984.  
  6985. class SelectDialogTreeNode(MfxTreeNode):
  6986.     
  6987.     def __init__(_, tree, text, select_func, expanded = 0, parent_node = None):
  6988.         MfxTreeNode.__init__(_, tree, parent_node, text, key = None, expanded = expanded)
  6989.         _.select_func = select_func
  6990.  
  6991.     
  6992.     def drawSymbol(_, x, y, **kw):
  6993.         if _.tree.nodes.get(_.symbol_id) is not _:
  6994.             _.symbol_id = _.tree.canvas.create_image(x, y, image = _.tree.data.img[_.expanded], anchor = 'nw')
  6995.             _.tree.nodes[_.symbol_id] = _
  6996.         
  6997.  
  6998.     
  6999.     def getContents(_):
  7000.         if _.subnodes is not None:
  7001.             return _.subnodes
  7002.         
  7003.         if type(_.select_func) in (types.TupleType, types.ListType):
  7004.             return _.select_func
  7005.         
  7006.         return _._getContents()
  7007.  
  7008.     
  7009.     def _getContents(_):
  7010.         return []
  7011.  
  7012.  
  7013.  
  7014. class SelectDialogTreeData:
  7015.     img = None
  7016.     
  7017.     def __init__(_):
  7018.         _.tree_xview = (0.0, 1.0)
  7019.         _.tree_yview = (0.0, 1.0)
  7020.         if _.img is None:
  7021.             SelectDialogTreeData.img = (Tkinter.PhotoImage(data = '\nR0lGODlhEAAOAPIFAAAAAICAgMDAwP//AP///4AAAAAAAAAAACH5BAEAAAUALAAAAAAQAA4AAAOL\nWLrcGxA6FoYYYoRZwhCDMAhDFCkBoa6sGgBFQAzCIAzCIAzCEACFAEEwEAwEA8FAMBAEAIUAYSAY\nCAaCgWAgGAQAhQBBMBAMBAPBQDAQBACFAGEgGAgGgoFgIBgEAAUBBAIDAgMCAwIDAgMCAQAFAQQD\nAgMCAwIDAgMCAwEABSaiogAKAKeoqakFCQA7'), Tkinter.PhotoImage(data = '\nR0lGODlhEAAOAPIFAAAAAICAgMDAwP//AP///4AAAAAAAAAAACH5BAEAAAUALAAAAAAQAA4AAAN3\nWLrcHBA6Foi1YZZAxBCDQESREhCDMAiDcFkBUASEMAiDMAiDMAgBAGlIGgQAgZeSEAAIAoAAQTAQ\nDAQDwUAwAEAAhQBBMBAMBAPBQBAABACFAGEgGAgGgoFgIAAEAAoBBAMCAwIDAgMCAwEAAApERI4L\njpWWlgkAOw=='), Tkinter.PhotoImage(data = '\nR0lGODdhEAAOAPIAAAAAAAAAgICAgMDAwP///wAAAAAAAAAAACwAAAAAEAAOAAADTii63DowyiiA\nGCHrnQUQAxcQAAEQgAAIg+MCwkDMdD0LgDDUQG8LAMGg1gPYBADBgFbs1QQAwYDWBNQEAMHABrAR\nBADBwOsVAFzoqlqdAAA7'), Tkinter.PhotoImage(data = '\nR0lGODdhEAAOAPIAAAAAAAAAgICAgMDAwP8AAP///wAAAAAAACwAAAAAEAAOAAADVCi63DowyiiA\nGCHrnQUQAxcUQAEUgAAIg+MCwlDMdD0LgDDQBE3UAoBgUCMUCDYBQDCwEWwFAUAwqBEKBJsAIBjQ\nCDRCTQAQDKBQAcDFBrjf8Lg7AQA7'))
  7022.         
  7023.  
  7024.  
  7025.  
  7026. class SelectDialogTreeCanvas(MfxTreeInCanvas):
  7027.     
  7028.     def __init__(_, dialog, parent, key, default, font = None, width = -1, height = -1, vbar = 1, hbar = 0):
  7029.         _.dialog = dialog
  7030.         _.default = default
  7031.         _.n_selections = 0
  7032.         _.n_expansions = 0
  7033.         disty = 16
  7034.         if width < 0:
  7035.             width = 400
  7036.         
  7037.         if height < 0:
  7038.             height = 20 * disty
  7039.             if parent and parent.winfo_screenheight() >= 600:
  7040.                 height = 25 * disty
  7041.             
  7042.             if parent and parent.winfo_screenheight() >= 800:
  7043.                 height = 30 * disty
  7044.             
  7045.         
  7046.         _.lines = height / disty
  7047.         MfxTreeInCanvas.__init__(_, parent, _.data.rootnodes, c_width = width, c_height = height, vbar = vbar, hbar = hbar)
  7048.         _.style.distx = 20
  7049.         _.style.disty = disty
  7050.         _.style.width = 16
  7051.         _.style.height = 14
  7052.         if font:
  7053.             _.style.font = font
  7054.         
  7055.         _.draw()
  7056.         _.updateSelection(key)
  7057.         if _.hbar:
  7058.             _.canvas.xview_moveto(_.data.tree_xview[0])
  7059.         
  7060.         if _.vbar:
  7061.             _.canvas.yview_moveto(_.data.tree_yview[0])
  7062.         
  7063.         _.showVbar()
  7064.         _.showHbar()
  7065.  
  7066.     
  7067.     def destroy(_):
  7068.         if _.n_expansions > 0:
  7069.             _.data.tree_xview = _.canvas.xview()
  7070.             _.data.tree_yview = _.canvas.yview()
  7071.         
  7072.         MfxTreeInCanvas.destroy(_)
  7073.  
  7074.     
  7075.     def getContents(_, node):
  7076.         return node.getContents()
  7077.  
  7078.     
  7079.     def singleClick(_, event = None):
  7080.         node = _.findNode()
  7081.         if isinstance(node, MfxTreeLeaf):
  7082.             if not (node.selected) and node.key is not None:
  7083.                 oldcur = _.canvas['cursor']
  7084.                 _.canvas['cursor'] = 'watch'
  7085.                 _.canvas.update_idletasks()
  7086.                 _.n_selections = _.n_selections + 1
  7087.                 _.updateSelection(node.key)
  7088.                 _.dialog.updatePreview(_.selection_key)
  7089.                 _.canvas['cursor'] = oldcur
  7090.             
  7091.         elif isinstance(node, MfxTreeNode):
  7092.             _.n_expansions = _.n_expansions + 1
  7093.             node.expanded = not (node.expanded)
  7094.             _.redraw()
  7095.         
  7096.         return 'break'
  7097.  
  7098.     
  7099.     def doubleClick(_, event = None):
  7100.         node = _.findNode()
  7101.         if isinstance(node, MfxTreeLeaf):
  7102.             if node.key is not None:
  7103.                 _.n_selections = _.n_selections + 1
  7104.                 _.updateSelection(node.key)
  7105.                 _.dialog.mDone(_.default)
  7106.             
  7107.         elif isinstance(node, MfxTreeNode):
  7108.             _.n_expansions = _.n_expansions + 1
  7109.             node.expanded = not (node.expanded)
  7110.             _.redraw()
  7111.         
  7112.         return 'break'
  7113.  
  7114.  
  7115.  
  7116. class SelectDialogPreviewCanvas(MfxScrolledCanvas):
  7117.     
  7118.     def createCanvas(_, kw):
  7119.         _.canvas = apply(MfxCanvas, (_.frame,), kw)
  7120.         _.canvas.grid(row = 0, column = 0, sticky = 'nsew')
  7121.  
  7122.     
  7123.     def pack(_, kw):
  7124.         _.frame.pack(side = 'right', fill = Tkinter.BOTH, expand = 1, padx = kw.padx, pady = kw.pady)
  7125.  
  7126.     
  7127.     def setup(_, app, kw):
  7128.         _.canvas.config(bg = app.opt.tablecolor)
  7129.         tile = app.tabletile_manager.get(app.tabletile_index)
  7130.         if tile:
  7131.             _.canvas.setTile(tile.filename)
  7132.             _.canvas.setTextColor(tile.text_color)
  7133.         
  7134.         _.pack(kw)
  7135.  
  7136.  
  7137.  
  7138. class SelectGameLeaf(SelectDialogTreeLeaf):
  7139.     pass
  7140.  
  7141.  
  7142. class SelectGameNode(SelectDialogTreeNode):
  7143.     
  7144.     def _getContents(_):
  7145.         contents = []
  7146.         if not contents:
  7147.             pass
  7148.         return _.tree.data.no_games
  7149.  
  7150.  
  7151.  
  7152. class SelectGameData(SelectDialogTreeData):
  7153.     
  7154.     def __init__(_, app):
  7155.         SelectDialogTreeData.__init__(_)
  7156.         _.all_games_gi = map(app.gdb.get, app.gdb.getGamesIdSortedByName())
  7157.         _.no_games = [
  7158.             SelectGameLeaf(None, None, '(no games)', None)]
  7159.         s_by_type = s_special = s_original = s_contrib = None
  7160.         g = []
  7161.         for data in (GI.SELECT_GAME_BY_TYPE, GI.SELECT_SPECIAL_GAME_BY_TYPE, GI.SELECT_ORIGINAL_GAME_BY_TYPE, GI.SELECT_CONTRIB_GAME_BY_TYPE):
  7162.             gg = []
  7163.             for name, select_func in data:
  7164.                 name = re.sub('&', '', name)
  7165.                 gg.append(SelectGameNode(None, name, select_func))
  7166.             
  7167.             g.append(gg)
  7168.         
  7169.         if 1 and g[1]:
  7170.             s_special = SelectGameNode(None, 'Special Games', tuple(g[1]))
  7171.         
  7172.         if 1 and g[2]:
  7173.             s_original = SelectGameNode(None, 'Original Games', tuple(g[2]))
  7174.         
  7175.         if 1 and g[3]:
  7176.             pass
  7177.         
  7178.         (s_by_compatibility, gg) = (None, [])
  7179.         for name, games in GI.GAMES_BY_COMPATIBILITY:
  7180.             
  7181.             select_func = lambda gi, games = games: gi.id in games
  7182.             gg.append(SelectGameNode(None, name, select_func))
  7183.         
  7184.         if 1 and gg:
  7185.             s_by_compatibility = SelectGameNode(None, 'by Compatibility', tuple(gg))
  7186.         
  7187.         (s_by_pysol_version, gg) = (None, [])
  7188.         for name, games in GI.GAMES_BY_PYSOL_VERSION:
  7189.             
  7190.             select_func = lambda gi, games = games: gi.id in games
  7191.             name = 'New games in v' + name
  7192.             gg.append(SelectGameNode(None, name, select_func))
  7193.         
  7194.         if 1 and gg and PACKAGE == 'PySol':
  7195.             s_by_pysol_version = SelectGameNode(None, 'by PySol version', tuple(gg))
  7196.         
  7197.         ul_alternate_names = UserList(list(app.gdb.getGamesTuplesSortedByAlternateName()))
  7198.         _.rootnodes = filter(None, (SelectGameNode(None, 'All Games', (lambda gi: 1), expanded = 0), SelectGameNode(None, 'Alternate Names', ul_alternate_names), SelectGameNode(None, 'Popular Games', (lambda gi: gi.si.game_flags & GI.GT_POPULAR), expanded = 0), s_special, s_by_type, SelectGameNode(None, 'by Game Feature', (SelectGameNode(None, 'by Number of Cards', (SelectGameNode(None, '32 cards', (lambda gi: gi.si.ncards == 32)), SelectGameNode(None, '48 cards', (lambda gi: gi.si.ncards == 48)), SelectGameNode(None, '52 cards', (lambda gi: gi.si.ncards == 52)), SelectGameNode(None, '64 cards', (lambda gi: gi.si.ncards == 64)), SelectGameNode(None, '78 cards', (lambda gi: gi.si.ncards == 78)), SelectGameNode(None, '104 cards', (lambda gi: gi.si.ncards == 104)), SelectGameNode(None, '144 cards', (lambda gi: gi.si.ncards == 144)), SelectGameNode(None, 'Other number', (lambda gi: gi.si.ncards not in (32, 48, 52, 64, 78, 104, 144))))), SelectGameNode(None, 'by Number of Decks', (SelectGameNode(None, '1 deck games', (lambda gi: gi.si.decks == 1)), SelectGameNode(None, '2 deck games', (lambda gi: gi.si.decks == 2)), SelectGameNode(None, '3 deck games', (lambda gi: gi.si.decks == 3)), SelectGameNode(None, '4 deck games', (lambda gi: gi.si.decks == 4)))), SelectGameNode(None, 'by Number of Redeals', (SelectGameNode(None, 'No redeal', (lambda gi: gi.si.redeals == 0)), SelectGameNode(None, '1 redeal', (lambda gi: gi.si.redeals == 1)), SelectGameNode(None, '2 redeals', (lambda gi: gi.si.redeals == 2)), SelectGameNode(None, '3 redeals', (lambda gi: gi.si.redeals == 3)), SelectGameNode(None, 'Unlimited redeals', (lambda gi: gi.si.redeals == -1)), SelectGameNode(None, 'Other number', (lambda gi: gi.si.redeals not in (-1, 0, 1, 2, 3))))), s_by_compatibility)), s_by_pysol_version, SelectGameNode(None, 'Other Categories', (SelectGameNode(None, 'Games for Children (very easy)', (lambda gi: gi.si.game_flags & GI.GT_CHILDREN)), SelectGameNode(None, 'Games with Scoring', (lambda gi: gi.si.game_flags & GI.GT_SCORE)), SelectGameNode(None, 'Games with Separate Decks', (lambda gi: gi.si.game_flags & GI.GT_SEPARATE_DECKS)), SelectGameNode(None, 'Open Games (all cards visible)', (lambda gi: gi.si.game_flags & GI.GT_OPEN)), SelectGameNode(None, 'Relaxed Variants', (lambda gi: gi.si.game_flags & GI.GT_RELAXED)))), s_original, s_contrib))
  7199.  
  7200.  
  7201.  
  7202. class SelectGameTreeWithPreview(SelectDialogTreeCanvas):
  7203.     data = None
  7204.     html_viewer = None
  7205.  
  7206.  
  7207. class SelectGameTree(SelectGameTreeWithPreview):
  7208.     
  7209.     def singleClick(_, event = None):
  7210.         _.doubleClick(event)
  7211.  
  7212.  
  7213.  
  7214. class SelectGameDialog(MfxDialog):
  7215.     Tree_Class = SelectGameTree
  7216.     TreeDataHolder_Class = SelectGameTreeWithPreview
  7217.     TreeData_Class = SelectGameData
  7218.     
  7219.     def __init__(_, parent, title, app, gameid, **kw):
  7220.         kw = _.initKw(kw)
  7221.         _ToplevelDialog.__init__(_, parent, title, kw.resizable, kw.default)
  7222.         (top_frame, bottom_frame) = _.createFrames(kw)
  7223.         _.createBitmaps(top_frame, kw)
  7224.         _.gameid = gameid
  7225.         _.app = app
  7226.         _.random = None
  7227.         if _.TreeDataHolder_Class.data is None:
  7228.             _.TreeDataHolder_Class.data = _.TreeData_Class(app)
  7229.         
  7230.         _.top.wm_minsize(200, 200)
  7231.         _.tree = _.Tree_Class(_, top_frame, key = gameid, default = kw.default, font = kw.font, vbar = 1, hbar = 0)
  7232.         _.tree.frame.pack(fill = Tkinter.BOTH, expand = 1, padx = kw.padx, pady = kw.pady)
  7233.         focus = _.createButtons(bottom_frame, kw)
  7234.         focus = _.tree.frame
  7235.         _.mainloop(focus, kw.timeout)
  7236.  
  7237.     
  7238.     def initKw(_, kw):
  7239.         kw = KwStruct(kw, strings = (None, None, 'Cancel'), default = 0, separatorwidth = 2, resizable = 1, font = None, padx = 10, pady = 10, buttonpadx = 10, buttonpady = 5)
  7240.         return MfxDialog.initKw(_, kw)
  7241.  
  7242.     
  7243.     def destroy(_):
  7244.         _.app = None
  7245.         _.tree.updateNodesWithTree(_.tree.rootnodes, None)
  7246.         _.tree.destroy()
  7247.         MfxDialog.destroy(_)
  7248.  
  7249.     
  7250.     def mDone(_, button):
  7251.         if button == 0:
  7252.             _.gameid = _.tree.selection_key
  7253.             _.tree.n_expansions = 1
  7254.         
  7255.         if button == 1:
  7256.             doc = _.app.getGameRulesFilename(_.tree.selection_key)
  7257.             if not doc:
  7258.                 return None
  7259.             
  7260.             dir = os.path.join('html', 'rules')
  7261.             viewer = SelectGameTreeWithPreview.html_viewer
  7262.             if viewer is None:
  7263.                 SelectGameTreeWithPreview.html_viewer = helpHTML(_.app, doc, dir)
  7264.             else:
  7265.                 url = _.app.dataloader.findFile(doc, dir)
  7266.                 viewer.updateHistoryXYView()
  7267.                 viewer.display(url, relpath = 0)
  7268.             return None
  7269.         
  7270.         MfxDialog.mDone(_, button)
  7271.  
  7272.  
  7273.  
  7274. class SelectGameDialogWithPreview(SelectGameDialog):
  7275.     Tree_Class = SelectGameTreeWithPreview
  7276.     
  7277.     def __init__(_, parent, title, app, gameid, bookmark = None, **kw):
  7278.         kw = _.initKw(kw)
  7279.         _ToplevelDialog.__init__(_, parent, title, kw.resizable, kw.default)
  7280.         (top_frame, bottom_frame) = _.createFrames(kw)
  7281.         _.createBitmaps(top_frame, kw)
  7282.         _.app = app
  7283.         _.gameid = gameid
  7284.         _.bookmark = bookmark
  7285.         _.random = None
  7286.         if _.TreeDataHolder_Class.data is None:
  7287.             _.TreeDataHolder_Class.data = _.TreeData_Class(app)
  7288.         
  7289.         _.top.wm_minsize(400, 200)
  7290.         sw = _.top.winfo_screenwidth()
  7291.         if sw >= 1100:
  7292.             (w1, w2) = (250, 600)
  7293.         elif sw >= 900:
  7294.             (w1, w2) = (250, 500)
  7295.         elif sw >= 800:
  7296.             (w1, w2) = (220, 480)
  7297.         else:
  7298.             (w1, w2) = (200, 300)
  7299.         w2 = max(200, min(w2, 10 + 12 * (app.subsampled_images.CARDW + 10)))
  7300.         _.tree = _.Tree_Class(_, top_frame, key = gameid, default = kw.default, font = kw.font, width = w1, vbar = 1, hbar = 0)
  7301.         _.tree.frame.pack(side = 'left', fill = Tkinter.BOTH, expand = 0, padx = kw.padx, pady = kw.pady)
  7302.         _.preview = SelectDialogPreviewCanvas(top_frame, width = w2, vbar = 1, hbar = 1)
  7303.         _.preview.setup(app, kw)
  7304.         _.preview.canvas.preview = 2
  7305.         _.preview_key = -1
  7306.         _.preview_game = None
  7307.         _.preview_app = None
  7308.         _.updatePreview(gameid, animations = 0)
  7309.         focus = _.createButtons(bottom_frame, kw)
  7310.         _.mainloop(focus, kw.timeout)
  7311.  
  7312.     
  7313.     def initKw(_, kw):
  7314.         kw = KwStruct(kw, strings = ('Select', None, 'Cancel'), default = 0)
  7315.         return SelectGameDialog.initKw(_, kw)
  7316.  
  7317.     
  7318.     def destroy(_):
  7319.         _.deletePreview(destroy = 1)
  7320.         _.preview.unbind()
  7321.         SelectGameDialog.destroy(_)
  7322.  
  7323.     
  7324.     def deletePreview(_, destroy = 0):
  7325.         _.preview_key = -1
  7326.         if _.preview:
  7327.             unbind_destroy(_.preview.canvas)
  7328.             _.preview.canvas.deleteAllItems()
  7329.             if destroy:
  7330.                 _.preview.canvas.delete('all')
  7331.             
  7332.         
  7333.         if _.preview_game:
  7334.             _.preview_game.endGame()
  7335.             _.preview_game.destruct()
  7336.             destruct(_.preview_game)
  7337.         
  7338.         _.preview_game = None
  7339.         if destroy:
  7340.             if _.preview_app:
  7341.                 destruct(_.preview_app)
  7342.             
  7343.             _.preview_app = None
  7344.         
  7345.  
  7346.     
  7347.     def updatePreview(_, gameid, animations = 5):
  7348.         if gameid == _.preview_key:
  7349.             return None
  7350.         
  7351.         _.deletePreview()
  7352.         canvas = _.preview.canvas
  7353.         gi = _.app.gdb.get(gameid)
  7354.         if not gi:
  7355.             _.preview_key = -1
  7356.             return None
  7357.         
  7358.         if _.preview_app is None:
  7359.             _.preview_app = Struct(audio = _.app.audio, canvas = canvas, cardset = _.app.cardset.copy(), comments = _.app.comments.new(), debug = 0, gamerandom = _.app.gamerandom, gdb = _.app.gdb, gimages = _.app.gimages, images = _.app.subsampled_images, menubar = None, miscrandom = _.app.miscrandom, opt = _.app.opt.copy(), startup_opt = _.app.startup_opt, stats = _.app.stats.new(), top = None, top_cursor = _.app.top_cursor, toolbar = None, constructGame = _.app.constructGame)
  7360.             _.preview_app.opt.shadow = 0
  7361.             _.preview_app.opt.shade = 0
  7362.         
  7363.         _.preview_app.audio = None
  7364.         if animations >= 0:
  7365.             _.preview_app.opt.animations = animations
  7366.         
  7367.         if _.preview_game:
  7368.             _.preview_game.endGame()
  7369.             _.preview_game.destruct()
  7370.         
  7371.         _.top.wm_title('Playable Preview - ' + _.app.getGameTitleName(gameid))
  7372.         if 1:
  7373.             (cw, ch) = (canvas.winfo_width(), canvas.winfo_height())
  7374.             if cw >= 100 and ch >= 100:
  7375.                 MfxCanvasText(canvas, cw / 2, ch - 4, preview = 0, anchor = 's', text = 'Playable Area', font = getFont('canvas_large'))
  7376.             
  7377.         
  7378.         _.preview_game = gi.gameclass(gi)
  7379.         _.preview_game.createPreview(_.preview_app)
  7380.         (tx, ty) = (0, 0)
  7381.         (gw, gh) = (_.preview_game.width, _.preview_game.height)
  7382.         canvas.config(scrollregion = (-tx, -ty, -tx, -ty))
  7383.         canvas.xview_moveto(0)
  7384.         canvas.yview_moveto(0)
  7385.         random = None
  7386.         if gameid == _.gameid:
  7387.             random = _.app.game.random.copy()
  7388.         
  7389.         if gameid == _.gameid and _.bookmark:
  7390.             _.preview_game.restoreGameFromBookmark(_.bookmark)
  7391.         else:
  7392.             _.preview_game.newGame(random = random, autoplay = 1)
  7393.         canvas.config(scrollregion = (-tx, -ty, gw, gh))
  7394.         _.preview.showVbar()
  7395.         _.preview.showHbar()
  7396.         _.preview_app.audio = _.app.audio
  7397.         if _.app.opt.animations:
  7398.             _.preview_app.opt.animations = 5
  7399.         else:
  7400.             _.preview_app.opt.animations = 0
  7401.         _.random = _.preview_game.random.copy()
  7402.         _.random.origin = _.random.ORIGIN_PREVIEW
  7403.         _.preview_key = gameid
  7404.  
  7405.  
  7406.  
  7407. class SelectCardsetLeaf(SelectDialogTreeLeaf):
  7408.     pass
  7409.  
  7410.  
  7411. class SelectCardsetNode(SelectDialogTreeNode):
  7412.     
  7413.     def _getContents(_):
  7414.         contents = []
  7415.         for obj in _.tree.data.all_objects:
  7416.             pass
  7417.         
  7418.         if not contents:
  7419.             pass
  7420.         return _.tree.data.no_contents
  7421.  
  7422.  
  7423.  
  7424. class SelectCardsetData(SelectDialogTreeData):
  7425.     
  7426.     def __init__(_, manager, key):
  7427.         SelectDialogTreeData.__init__(_)
  7428.         _.all_objects = manager.getAllSortedByName()
  7429.         _.all_objects = filter((lambda obj: not (obj.error)), _.all_objects)
  7430.         _.no_contents = [
  7431.             SelectCardsetLeaf(None, None, '(no cardsets)', key = None)]
  7432.         select_by_type = None
  7433.         items = CSI.TYPE.items()
  7434.         items.sort((lambda a, b: cmp(a[1], b[1])))
  7435.         nodes = []
  7436.         for key, name in items:
  7437.             pass
  7438.         
  7439.         if nodes:
  7440.             select_by_type = SelectCardsetNode(None, 'by Type', tuple(nodes), expanded = 1)
  7441.         
  7442.         select_by_style = None
  7443.         items = CSI.STYLE.items()
  7444.         items.sort((lambda a, b: cmp(a[1], b[1])))
  7445.         nodes = []
  7446.         for key, name in items:
  7447.             pass
  7448.         
  7449.         if nodes:
  7450.             nodes.append(SelectCardsetNode(None, 'Uncategorized', (lambda cs: not (cs.si.styles))))
  7451.             select_by_style = SelectCardsetNode(None, 'by Style', tuple(nodes))
  7452.         
  7453.         select_by_nationality = None
  7454.         items = CSI.NATIONALITY.items()
  7455.         items.sort((lambda a, b: cmp(a[1], b[1])))
  7456.         nodes = []
  7457.         for key, name in items:
  7458.             pass
  7459.         
  7460.         if nodes:
  7461.             nodes.append(SelectCardsetNode(None, 'Uncategorized', (lambda cs: not (cs.si.nationalities))))
  7462.             select_by_nationality = SelectCardsetNode(None, 'by Nationality', tuple(nodes))
  7463.         
  7464.         select_by_date = None
  7465.         items = CSI.DATE.items()
  7466.         items.sort((lambda a, b: cmp(a[1], b[1])))
  7467.         nodes = []
  7468.         for key, name in items:
  7469.             pass
  7470.         
  7471.         if nodes:
  7472.             nodes.append(SelectCardsetNode(None, 'Uncategorized', (lambda cs: not (cs.si.dates))))
  7473.             select_by_date = SelectCardsetNode(None, 'by Date', tuple(nodes))
  7474.         
  7475.         _.rootnodes = filter(None, (SelectCardsetNode(None, 'All Cardsets', (lambda cs: 1), expanded = len(_.all_objects) <= 12), SelectCardsetNode(None, 'by Size', (SelectCardsetNode(None, 'Tiny cardsets', (lambda cs: cs.si.size == CSI.SIZE_TINY)), SelectCardsetNode(None, 'Small cardsets', (lambda cs: cs.si.size == CSI.SIZE_SMALL)), SelectCardsetNode(None, 'Medium cardsets', (lambda cs: cs.si.size == CSI.SIZE_MEDIUM)), SelectCardsetNode(None, 'Large cardsets', (lambda cs: cs.si.size == CSI.SIZE_LARGE)), SelectCardsetNode(None, 'XLarge cardsets', (lambda cs: cs.si.size == CSI.SIZE_XLARGE))), expanded = 1), select_by_type, select_by_style, select_by_date, select_by_nationality))
  7476.  
  7477.  
  7478.  
  7479. class SelectCardsetByTypeData(SelectDialogTreeData):
  7480.     
  7481.     def __init__(_, manager, key):
  7482.         SelectDialogTreeData.__init__(_)
  7483.         _.all_objects = manager.getAllSortedByName()
  7484.         _.no_contents = [
  7485.             SelectCardsetLeaf(None, None, '(no cardsets)', key = None)]
  7486.         items = CSI.TYPE.items()
  7487.         items.sort((lambda a, b: cmp(a[1], b[1])))
  7488.         nodes = []
  7489.         for key, name in items:
  7490.             pass
  7491.         
  7492.         select_by_type = SelectCardsetNode(None, 'by Type', tuple(nodes), expanded = 1)
  7493.         _.rootnodes = filter(None, (select_by_type,))
  7494.  
  7495.  
  7496.  
  7497. class SelectCardsetTree(SelectDialogTreeCanvas):
  7498.     data = None
  7499.  
  7500.  
  7501. class SelectCardsetByTypeTree(SelectDialogTreeCanvas):
  7502.     data = None
  7503.  
  7504.  
  7505. class SelectCardsetDialogWithPreview(MfxDialog):
  7506.     Tree_Class = SelectCardsetTree
  7507.     TreeDataHolder_Class = SelectCardsetTree
  7508.     TreeData_Class = SelectCardsetData
  7509.     
  7510.     def __init__(_, parent, title, app, manager, key = None, **kw):
  7511.         kw = _.initKw(kw)
  7512.         _ToplevelDialog.__init__(_, parent, title, kw.resizable, kw.default)
  7513.         (top_frame, bottom_frame) = _.createFrames(kw)
  7514.         _.createBitmaps(top_frame, kw)
  7515.         if key is None:
  7516.             key = manager.getSelected()
  7517.         
  7518.         _.manager = manager
  7519.         _.key = key
  7520.         if _.TreeDataHolder_Class.data is None:
  7521.             _.TreeDataHolder_Class.data = _.TreeData_Class(manager, key)
  7522.         
  7523.         _.top.wm_minsize(400, 200)
  7524.         if _.top.winfo_screenwidth() >= 800:
  7525.             (w1, w2) = (200, 400)
  7526.         else:
  7527.             (w1, w2) = (200, 300)
  7528.         _.tree = _.Tree_Class(_, top_frame, key = key, default = kw.default, font = kw.font, width = w1)
  7529.         _.tree.frame.pack(side = 'left', fill = Tkinter.BOTH, expand = 0, padx = kw.padx, pady = kw.pady)
  7530.         _.preview = SelectDialogPreviewCanvas(top_frame, width = w2, hbar = 1)
  7531.         _.preview.setup(app, kw)
  7532.         _.preview_key = -1
  7533.         _.preview_images = []
  7534.         _.updatePreview(key)
  7535.         focus = _.createButtons(bottom_frame, kw)
  7536.         focus = _.tree.frame
  7537.         _.mainloop(focus, kw.timeout)
  7538.  
  7539.     
  7540.     def destroy(_):
  7541.         _.tree.updateNodesWithTree(_.tree.rootnodes, None)
  7542.         _.tree.destroy()
  7543.         _.preview.unbind()
  7544.         _.preview_images = []
  7545.         MfxDialog.destroy(_)
  7546.  
  7547.     
  7548.     def initKw(_, kw):
  7549.         kw = KwStruct(kw, strings = ('OK', 'Load', 'Cancel'), default = 0, separatorwidth = 2, resizable = 1, font = None, padx = 10, pady = 10, buttonpadx = 10, buttonpady = 5)
  7550.         return MfxDialog.initKw(_, kw)
  7551.  
  7552.     
  7553.     def mDone(_, button):
  7554.         if button in (0, 1):
  7555.             _.key = _.tree.selection_key
  7556.             _.tree.n_expansions = 1
  7557.         
  7558.         if button in (3, 4):
  7559.             cs = _.manager.get(_.tree.selection_key)
  7560.             if not cs:
  7561.                 return None
  7562.             
  7563.             f = os.path.join(cs.dir, 'COPYRIGHT')
  7564.             if button == 4:
  7565.                 f = os.path.join(cs.dir, 'config.txt')
  7566.             
  7567.             
  7568.             try:
  7569.                 file = open(f)
  7570.                 text = file.read()
  7571.                 file.close()
  7572.             except:
  7573.                 return None
  7574.  
  7575.             d = DisplayTextDialog(_.top, title = CARDSET + ' ' + cs.name, text = text)
  7576.             return None
  7577.         
  7578.         MfxDialog.mDone(_, button)
  7579.  
  7580.     
  7581.     def updatePreview(_, key):
  7582.         if key == _.preview_key:
  7583.             return None
  7584.         
  7585.         canvas = _.preview.canvas
  7586.         canvas.deleteAllItems()
  7587.         _.preview_images = []
  7588.         cs = _.manager.get(key)
  7589.         if not cs:
  7590.             _.preview_key = -1
  7591.             return None
  7592.         
  7593.         (names, columns) = cs.getPreviewCardNames()
  7594.         
  7595.         try:
  7596.             (names, columns) = cs.getPreviewCardNames()
  7597.             for n in names:
  7598.                 f = os.path.join(cs.dir, n + cs.ext)
  7599.                 _.preview_images.append(Tkinter.PhotoImage(file = f))
  7600.         except:
  7601.             _.preview_key = -1
  7602.             _.preview_images = []
  7603.             return None
  7604.  
  7605.         (i, x, y, sx, sy, dx, dy) = (0, 10, 10, 0, 0, cs.CARDW + 10, cs.CARDH + 10)
  7606.         for image in _.preview_images:
  7607.             MfxCanvasImage(canvas, x, y, anchor = 'nw', image = image)
  7608.             (sx, sy) = (max(x, sx), max(y, sy))
  7609.             i = i + 1
  7610.         
  7611.         canvas.config(scrollregion = (0, 0, sx + dx, sy + dy))
  7612.         canvas.config(xscrollincrement = dx, yscrollincrement = dy)
  7613.         _.preview.showVbar()
  7614.         _.preview.showHbar()
  7615.         _.preview_key = key
  7616.  
  7617.  
  7618.  
  7619. class SelectCardsetByTypeDialogWithPreview(SelectCardsetDialogWithPreview):
  7620.     Tree_Class = SelectCardsetByTypeTree
  7621.     TreeDataHolder_Class = SelectCardsetByTypeTree
  7622.     TreeData_Class = SelectCardsetByTypeData
  7623.  
  7624.  
  7625. class SelectTileLeaf(SelectDialogTreeLeaf):
  7626.     pass
  7627.  
  7628.  
  7629. class SelectTileNode(SelectDialogTreeNode):
  7630.     
  7631.     def _getContents(_):
  7632.         contents = []
  7633.         for obj in _.tree.data.all_objects:
  7634.             pass
  7635.         
  7636.         if not contents:
  7637.             pass
  7638.         return _.tree.data.no_contents
  7639.  
  7640.  
  7641.  
  7642. class SelectTileData(SelectDialogTreeData):
  7643.     
  7644.     def __init__(_, manager, key):
  7645.         SelectDialogTreeData.__init__(_)
  7646.         _.all_objects = manager.getAllSortedByName()
  7647.         _.all_objects = filter((lambda obj: not (obj.error)), _.all_objects)
  7648.         _.all_objects = filter((lambda tile: if tile.index > 0:
  7649. passtile.filename), _.all_objects)
  7650.         _.no_contents = [
  7651.             SelectTileLeaf(None, None, '(no tiles)', key = None)]
  7652.         if not type(key) is types.StringType:
  7653.             pass
  7654.         e1 = len(_.all_objects) <= 17
  7655.         e2 = 1
  7656.         _.rootnodes = (SelectTileNode(None, 'Solid Colors', (SelectTileLeaf(None, None, 'Blue', key = '#0082df'), SelectTileLeaf(None, None, 'Green', key = '#008200'), SelectTileLeaf(None, None, 'Navy', key = '#000086'), SelectTileLeaf(None, None, 'Olive', key = '#868200'), SelectTileLeaf(None, None, 'Orange', key = '#f79600'), SelectTileLeaf(None, None, 'Teal', key = '#008286')), expanded = e1), SelectTileNode(None, 'All Backgrounds', (lambda tile: 1), expanded = e2))
  7657.  
  7658.  
  7659.  
  7660. class SelectTileTree(SelectDialogTreeCanvas):
  7661.     data = None
  7662.  
  7663.  
  7664. class SelectTileDialogWithPreview(MfxDialog):
  7665.     Tree_Class = SelectTileTree
  7666.     TreeDataHolder_Class = SelectTileTree
  7667.     TreeData_Class = SelectTileData
  7668.     
  7669.     def __init__(_, parent, title, app, manager, key = None, **kw):
  7670.         kw = _.initKw(kw)
  7671.         _ToplevelDialog.__init__(_, parent, title, kw.resizable, kw.default)
  7672.         (top_frame, bottom_frame) = _.createFrames(kw)
  7673.         _.createBitmaps(top_frame, kw)
  7674.         if key is None:
  7675.             key = manager.getSelected()
  7676.         
  7677.         _.manager = manager
  7678.         _.key = key
  7679.         _.tablecolor = app.opt.tablecolor
  7680.         if _.TreeDataHolder_Class.data is None:
  7681.             _.TreeDataHolder_Class.data = _.TreeData_Class(manager, key)
  7682.         
  7683.         _.top.wm_minsize(400, 200)
  7684.         if _.top.winfo_screenwidth() >= 800:
  7685.             (w1, w2) = (200, 400)
  7686.         else:
  7687.             (w1, w2) = (200, 300)
  7688.         _.tree = _.Tree_Class(_, top_frame, key = key, default = kw.default, font = kw.font, width = w1)
  7689.         _.tree.frame.pack(side = 'left', fill = Tkinter.BOTH, expand = 0, padx = kw.padx, pady = kw.pady)
  7690.         _.preview = SelectDialogPreviewCanvas(top_frame, width = w2, vbar = 0, hbar = 0)
  7691.         _.preview.pack(kw)
  7692.         _.preview_key = -1
  7693.         _.updatePreview(key)
  7694.         focus = _.createButtons(bottom_frame, kw)
  7695.         focus = _.tree.frame
  7696.         _.mainloop(focus, kw.timeout)
  7697.  
  7698.     
  7699.     def destroy(_):
  7700.         _.tree.updateNodesWithTree(_.tree.rootnodes, None)
  7701.         _.tree.destroy()
  7702.         _.preview.unbind()
  7703.         MfxDialog.destroy(_)
  7704.  
  7705.     
  7706.     def initKw(_, kw):
  7707.         kw = KwStruct(kw, strings = ('OK', 'Solid color...', 'Cancel'), default = 0, separatorwidth = 2, resizable = 1, font = None, padx = 10, pady = 10, buttonpadx = 10, buttonpady = 5)
  7708.         return MfxDialog.initKw(_, kw)
  7709.  
  7710.     
  7711.     def mDone(_, button):
  7712.         if button == 0:
  7713.             _.key = _.tree.selection_key
  7714.             _.tree.n_expansions = 1
  7715.         
  7716.         if button == 1:
  7717.             c = tkColorChooser.askcolor(master = _.top, initialcolor = _.tablecolor, title = 'Select table color')
  7718.             if c and c[1]:
  7719.                 _.key = string.lower(c[1])
  7720.                 _.tablecolor = _.key
  7721.                 _.tree.updateSelection(_.key)
  7722.                 _.updatePreview(_.key)
  7723.             
  7724.             return None
  7725.         
  7726.         MfxDialog.mDone(_, button)
  7727.  
  7728.     
  7729.     def updatePreview(_, key):
  7730.         if key == _.preview_key:
  7731.             return None
  7732.         
  7733.         canvas = _.preview.canvas
  7734.         if type(key) is types.StringType:
  7735.             canvas.config(bg = key)
  7736.             canvas.deleteAllItems()
  7737.             canvas.setTile(None)
  7738.             canvas.setTextColor(None)
  7739.             _.preview_key = key
  7740.             _.tablecolor = key
  7741.             return None
  7742.         
  7743.         im = None
  7744.         tile = _.manager.get(key)
  7745.         if tile:
  7746.             
  7747.             try:
  7748.                 im = Tkinter.PhotoImage(file = tile.filename)
  7749.             except Tkinter.TclError:
  7750.                 ex = None
  7751.                 im = None
  7752.  
  7753.         
  7754.         canvas.deleteAllItems()
  7755.         canvas.setTile(im)
  7756.         canvas.setTextColor(tile.text_color)
  7757.         _.preview_key = key
  7758.         if im is None:
  7759.             _.preview_key = -1
  7760.         
  7761.  
  7762.  
  7763.  
  7764. class SingleGame_StatsDialog(MfxDialog):
  7765.     
  7766.     def __init__(_, parent, title, app, player, gameid, **kw):
  7767.         kw = _.initKw(kw)
  7768.         _ToplevelDialog.__init__(_, parent, title, kw.resizable, kw.default)
  7769.         (top_frame, bottom_frame) = _.createFrames(kw)
  7770.         _.createBitmaps(top_frame, kw)
  7771.         _.top.wm_minsize(200, 200)
  7772.         _.button = kw.default
  7773.         createChart = _.create3DBarChart
  7774.         if parent.winfo_screenwidth() < 800 or parent.winfo_screenheight() < 600:
  7775.             createChart = _.createPieChart
  7776.             createChart = _.createSimpleChart
  7777.         
  7778.         (won, lost) = app.stats.getStats(player, gameid)
  7779.         createChart(top_frame, app, won, lost, 'Total')
  7780.         g = app.stats.session_games.get(player, [])
  7781.         g = filter((lambda a, b = gameid: a[0] == b), g)
  7782.         won = len(filter((lambda a, b = gameid: a[2] > 0), g))
  7783.         lost = len(filter((lambda a, b = gameid: a[2] == 0), g))
  7784.         createChart(top_frame, app, won, lost, 'Current session')
  7785.         focus = _.createButtons(bottom_frame, kw)
  7786.         _.mainloop(focus, kw.timeout)
  7787.  
  7788.     
  7789.     def _getPwon(_, won, lost):
  7790.         (pwon, plost) = (0.0, 0.0)
  7791.         if won + lost > 0:
  7792.             pwon = float(won) / (won + lost)
  7793.             pwon = min(max(pwon, 1e-05), 0.99999)
  7794.             plost = 1.0 - pwon
  7795.         
  7796.         return (pwon, plost)
  7797.  
  7798.     
  7799.     def _createChartInit(_, frame, w, h, text):
  7800.         tfont = getFont('tree_small')
  7801.         frame = Tkinter.Frame(frame)
  7802.         c = Tkinter.Canvas(frame, width = w, height = h)
  7803.         fg = c.cget('insertbackground')
  7804.         frame.pack(side = Tkinter.TOP, fill = Tkinter.BOTH, expand = 1, padx = 20, pady = 10)
  7805.         c.pack()
  7806.         c.create_rectangle(2, 7, w, h, fill = '', outline = '#7f7f7f')
  7807.         l = Tkinter.Label(c, text = text, font = tfont, bd = 0, padx = 3, pady = 1)
  7808.         c.create_window(20, 0, window = l, anchor = 'nw')
  7809.         return (c, tfont, fg)
  7810.  
  7811.     
  7812.     def _createChartTexts(_, c, tx, ty, won, lost):
  7813.         tfont = getFont('tree_small')
  7814.         fg = c.cget('insertbackground')
  7815.         (pwon, plost) = _._getPwon(won, lost)
  7816.         x = tx[0]
  7817.         c.create_text(x, ty[0], text = 'Won:', anchor = 'nw', font = tfont, fill = fg)
  7818.         c.create_text(x, ty[1], text = 'Lost:', anchor = 'nw', font = tfont, fill = fg)
  7819.         c.create_text(x, ty[2], text = 'Total:', anchor = 'nw', font = tfont, fill = fg)
  7820.         x = tx[1] - 16
  7821.         c.create_text(x, ty[0], text = '%d' % won, anchor = 'ne', font = tfont, fill = fg)
  7822.         c.create_text(x, ty[1], text = '%d' % lost, anchor = 'ne', font = tfont, fill = fg)
  7823.         c.create_text(x, ty[2], text = '%d' % (won + lost), anchor = 'ne', font = tfont, fill = fg)
  7824.         y = ty[2] - 11
  7825.         c.create_line(tx[0], y, x, y, fill = fg)
  7826.         if won + lost > 0:
  7827.             x = tx[2]
  7828.             pw = int(round(100.0 * pwon))
  7829.             c.create_text(x, ty[0], text = '%d%%' % pw, anchor = 'ne', font = tfont, fill = fg)
  7830.             c.create_text(x, ty[1], text = '%d%%' % (100 - pw), anchor = 'ne', font = tfont, fill = fg)
  7831.         
  7832.  
  7833.     
  7834.     def _createChart3DBar(_, canvas, perc, x, y, p, col):
  7835.         if perc < 0.005:
  7836.             return None
  7837.         
  7838.         p = list(p[:])
  7839.         for i in (0, 1, 2, 3):
  7840.             p[i] = (x + p[i][0], y + p[i][1])
  7841.             j = i + 4
  7842.             dx = int(round(p[j][0] * perc))
  7843.             dy = int(round(p[j][1] * perc))
  7844.             p[j] = (p[i][0] + dx, p[i][1] + dy)
  7845.         
  7846.         
  7847.         def draw_rect(a, b, c, d, col, canvas = canvas, p = p):
  7848.             points = (p[a][0], p[a][1], p[b][0], p[b][1], p[c][0], p[c][1], p[d][0], p[d][1])
  7849.             canvas.create_polygon(points, fill = col)
  7850.  
  7851.         draw_rect(0, 1, 5, 4, col[0])
  7852.         draw_rect(1, 2, 6, 5, col[1])
  7853.         draw_rect(4, 5, 6, 7, col[2])
  7854.         
  7855.         def draw_line(a, b, canvas = canvas, p = p):
  7856.             canvas.create_line(p[a][0], p[a][1], p[b][0], p[b][1])
  7857.  
  7858.         draw_line(0, 1)
  7859.         draw_line(1, 2)
  7860.         draw_line(0, 4)
  7861.         draw_line(1, 5)
  7862.         draw_line(2, 6)
  7863.         draw_line(4, 5)
  7864.         draw_line(5, 6)
  7865.         draw_line(6, 7)
  7866.         draw_line(7, 4)
  7867.  
  7868.     
  7869.     def createSimpleChart(_, frame, app, won, lost, text):
  7870.         (c, tfont, fg) = _._createChartInit(frame, 300, 100, text)
  7871.         tx = (90, 180, 210)
  7872.         ty = (21, 41, 75)
  7873.         _._createChartTexts(c, tx, ty, won, lost)
  7874.  
  7875.     
  7876.     def create3DBarChart(_, frame, app, won, lost, text):
  7877.         image = app.gimages.stats[0]
  7878.         (iw, ih) = (image.width(), image.height())
  7879.         (c, tfont, fg) = _._createChartInit(frame, iw + 160, ih, text)
  7880.         (pwon, plost) = _._getPwon(won, lost)
  7881.         tx = (iw + 20, iw + 110, iw + 140)
  7882.         yy = ih / 2
  7883.         ty = (yy + 21 - 46, yy + 41 - 46, yy + 75 - 46)
  7884.         c.create_image(0, 7, image = image, anchor = 'nw')
  7885.         p = ((0, 0), (44, 6), (62, -9), (20, -14), (-3, -118), (-1, -120), (-1, -114), (-4, -112))
  7886.         col = ('#00ff00', '#008200', '#00c300')
  7887.         _._createChart3DBar(c, pwon, 102, 145 + 7, p, col)
  7888.         p = ((0, 0), (49, 6), (61, -10), (15, -15), (1, -123), (3, -126), (4, -120), (1, -118))
  7889.         col = ('#ff0000', '#860400', '#c70400')
  7890.         _._createChart3DBar(c, plost, 216, 159 + 7, p, col)
  7891.         _._createChartTexts(c, tx, ty, won, lost)
  7892.  
  7893.     
  7894.     def createPieChart(_, frame, app, won, lost, text):
  7895.         (c, tfont, fg) = _._createChartInit(frame, 300, 100, text)
  7896.         (pwon, plost) = _._getPwon(won, lost)
  7897.         tx = (160, 250, 280)
  7898.         ty = (21, 41, 75)
  7899.         if won + lost > 0:
  7900.             (s, ewon, elost) = (0.0, 360.0 * pwon, 360.0 * plost)
  7901.             c.create_arc(20, 25 + 9, 110, 75 + 9, fill = '#007f00', start = s, extent = ewon)
  7902.             c.create_arc(20, 25 + 9, 110, 75 + 9, fill = '#7f0000', start = s + ewon, extent = elost)
  7903.             c.create_arc(20, 25, 110, 75, fill = '#00ff00', start = s, extent = ewon)
  7904.             c.create_arc(20, 25, 110, 75, fill = '#ff0000', start = s + ewon, extent = elost)
  7905.             (x, y) = (tx[0] - 25, ty[0])
  7906.             c.create_rectangle(x, y, x + 10, y + 10, fill = '#00ff00')
  7907.             y = ty[1]
  7908.             c.create_rectangle(x, y, x + 10, y + 10, fill = '#ff0000')
  7909.         else:
  7910.             c.create_oval(20, 25 + 10, 110, 75 + 10, fill = '#7f7f7f')
  7911.             c.create_oval(20, 25, 110, 75, fill = '#f0f0f0')
  7912.             c.create_text(65, 50, text = 'No games', anchor = 'center', font = tfont, fill = '#bfbfbf')
  7913.         _._createChartTexts(c, tx, ty, won, lost)
  7914.  
  7915.     
  7916.     def initKw(_, kw):
  7917.         kw = KwStruct(kw, strings = ('OK', ('All games...', 102), ('Reset...', 302)), default = 0, separatorwidth = 0, padx = 10, pady = 10)
  7918.         return MfxDialog.initKw(_, kw)
  7919.  
  7920.     
  7921.     def getDefaultFont(_):
  7922.         return getFont('small')
  7923.  
  7924.  
  7925.  
  7926. class AllGames_StatsDialogScrolledCanvas(MfxScrolledCanvas):
  7927.     pass
  7928.  
  7929.  
  7930. class AllGames_StatsDialog(MfxDialog):
  7931.     (CHAR_W, CHAR_H) = (7, 16)
  7932.     if os.name == 'mac':
  7933.         CHAR_W = 6
  7934.     
  7935.     YVIEW = 0
  7936.     
  7937.     def __init__(_, parent, title, app, player, **kw):
  7938.         lines = 25
  7939.         if parent and parent.winfo_screenheight() < 600:
  7940.             lines = 20
  7941.         
  7942.         kwdefault(kw, c_height = lines * _.CHAR_H)
  7943.         kw = _.initKw(kw)
  7944.         _ToplevelDialog.__init__(_, parent, title, kw.resizable, kw.default)
  7945.         (top_frame, bottom_frame) = _.createFrames(kw)
  7946.         _.createBitmaps(top_frame, kw)
  7947.         _.app = app
  7948.         _.top.wm_minsize(200, 200)
  7949.         _.button = kw.default
  7950.         _.font = kw.font
  7951.         _.sc = AllGames_StatsDialogScrolledCanvas(top_frame)
  7952.         _.canvas = _.sc.canvas
  7953.         _.canvas.config(width = kw.c_width, height = kw.c_height)
  7954.         _.sc.frame.pack(fill = Tkinter.BOTH, expand = 1, padx = kw.padx, pady = kw.pady)
  7955.         _.nodes = { }
  7956.         _.canvas.dialog = _
  7957.         bind(_.canvas, '<ButtonPress-1>', _.singleClick)
  7958.         _.fillCanvas(player, title)
  7959.         bbox = _.canvas.bbox('all')
  7960.         _.canvas.config(scrollregion = (0, 0, bbox[2], bbox[3]))
  7961.         _.canvas.yview_moveto(_.YVIEW)
  7962.         _.sc.showVbar()
  7963.         _.sc.showHbar()
  7964.         focus = _.createButtons(bottom_frame, kw)
  7965.         _.mainloop(focus, kw.timeout)
  7966.  
  7967.     
  7968.     def initKw(_, kw):
  7969.         kw = KwStruct(kw, strings = ('OK', ('Save to file', 202), ('Reset all...', 301)), default = 0, separatorwidth = 2, resizable = 1, padx = 10, pady = 10, c_width = 500)
  7970.         return MfxDialog.initKw(_, kw)
  7971.  
  7972.     
  7973.     def getDefaultFont(_):
  7974.         return getFont('small')
  7975.  
  7976.     
  7977.     def destroy(_):
  7978.         _.app = None
  7979.         _.canvas.dialog = None
  7980.         _.nodes = { }
  7981.         _.sc.destroy()
  7982.         MfxDialog.destroy(_)
  7983.  
  7984.     
  7985.     def singleClick(_, event = None):
  7986.         id = _.canvas.find_withtag(Tkinter.CURRENT)
  7987.         if not id:
  7988.             return None
  7989.         
  7990.         (gameid, gamenumber) = _.nodes.get(id[0], (None, None))
  7991.         return None
  7992.         if gameid and gamenumber:
  7993.             print gameid, gamenumber
  7994.         elif gameid:
  7995.             print gameid
  7996.         
  7997.  
  7998.     
  7999.     def fillCanvas(_, player, header):
  8000.         a = PysolStatsFormatter(_.app)
  8001.         writer = _.CanvasWriter(_.canvas, _.font, _.CHAR_H)
  8002.         if not a.writeStats(writer, player, header):
  8003.             writer.p('No entries for player ' + str(player) + '\n')
  8004.         
  8005.         destruct(writer)
  8006.         destruct(a)
  8007.  
  8008.     
  8009.     class CanvasWriter(PysolStatsFormatter.StringWriter):
  8010.         
  8011.         def __init__(_, canvas, font, h):
  8012.             _.canvas = canvas
  8013.             _.fg = canvas.cget('insertbackground')
  8014.             _.font = font
  8015.             _.h = h
  8016.             _.x = _.y = 0
  8017.             _.gameid = None
  8018.             _.gamenumber = None
  8019.             _.canvas.config(yscrollincrement = h)
  8020.  
  8021.         
  8022.         def _addItem(_, id):
  8023.             _.canvas.dialog.nodes[id] = (_.gameid, _.gamenumber)
  8024.  
  8025.         
  8026.         def p(_, s):
  8027.             if _.y > 16000:
  8028.                 return None
  8029.             
  8030.             (h1, h2) = (0, 0)
  8031.             while s and s[0] == '\n':
  8032.                 s = s[1:]
  8033.                 h1 = h1 + _.h
  8034.             while s and s[-1] == '\n':
  8035.                 s = s[:-1]
  8036.                 h2 = h2 + _.h
  8037.             _.y = _.y + h1
  8038.             if s:
  8039.                 id = _.canvas.create_text(_.x, _.y, text = s, anchor = 'nw', font = _.font, fill = _.fg)
  8040.                 _._addItem(id)
  8041.             
  8042.             _.y = _.y + h2
  8043.  
  8044.         
  8045.         def pheader(_, s):
  8046.             pass
  8047.  
  8048.         
  8049.         def pstats(_, t1, t2, t3, t4, t5, gameid = None):
  8050.             if _.y > 16000:
  8051.                 return None
  8052.             
  8053.             _.gameid = gameid
  8054.             _.gamenumber = None
  8055.             (x, y) = (1, _.y)
  8056.             p = _._pstats_text
  8057.             h = 0
  8058.             h = max(h, p(x, y, anchor = 'nw', text = str(t1)))
  8059.             h = max(h, p(x + 200, y, anchor = 'ne', text = str(t2)))
  8060.             h = max(h, p(x + 250, y, anchor = 'ne', text = str(t3)))
  8061.             h = max(h, p(x + 300, y, anchor = 'ne', text = str(t4)))
  8062.             h = max(h, p(x + 350, y, anchor = 'ne', text = str(t5)))
  8063.             _.pstats_perc(x + 372, y, str(t5))
  8064.             _.y = _.y + h
  8065.             _.gameid = None
  8066.  
  8067.         
  8068.         def _pstats_text(_, x, y, **kw):
  8069.             kwdefault(kw, font = _.font, fill = _.fg)
  8070.             id = apply(_.canvas.create_text, (x, y), kw)
  8071.             _._addItem(id)
  8072.             return _.h
  8073.  
  8074.         
  8075.         def pstats_perc(_, x, y, t):
  8076.             if t:
  8077.                 pass
  8078.             if not None if t[0] <= t[0] else t[0] <= '9':
  8079.                 return None
  8080.             
  8081.             perc = int(round(string.atof(str(t))))
  8082.             if perc < 1:
  8083.                 return None
  8084.             
  8085.             (rx, ry, rw, rh) = (x, y + 1, 2 + 8 * 10, _.h - 5)
  8086.             if 1:
  8087.                 w = int(round(rw * perc / 100.0))
  8088.                 if 1 and w < 1:
  8089.                     return None
  8090.                 
  8091.                 if w > 0:
  8092.                     w = max(3, w)
  8093.                     w = min(rw - 2, w)
  8094.                     id = _.canvas.create_rectangle(rx, ry, rx + w, ry + rh, width = 1, fill = '#00ff00', outline = '#000000')
  8095.                 
  8096.                 if w < rw:
  8097.                     id = _.canvas.create_rectangle(rx + w, ry, rx + rw, ry + rh, width = 1, fill = '#ff0000', outline = '#000000')
  8098.                 
  8099.                 return None
  8100.             
  8101.             fill = None
  8102.             id = _.canvas.create_rectangle(rx, ry, rx + rw, ry + rh, width = 1, fill = fill, outline = '#808080')
  8103.             if 1:
  8104.                 (rx, rw) = (rx + 1, rw - 1)
  8105.                 (ry, rh) = (ry + 1, rh - 1)
  8106.                 w = int(round(rw * perc / 100.0))
  8107.                 if w > 0:
  8108.                     id = _.canvas.create_rectangle(rx, ry, rx + w, ry + rh, width = 0, fill = '#00ff00', outline = '')
  8109.                 
  8110.                 if w < rw:
  8111.                     id = _.canvas.create_rectangle(rx + w, ry, rx + rw, ry + rh, width = 0, fill = '#ff0000', outline = '')
  8112.                 
  8113.                 return None
  8114.             
  8115.             p = 1.0
  8116.             ix = rx + 2
  8117.             for i in (1, 11, 21, 31, 41, 51, 61, 71, 81, 91):
  8118.                 (r, g, b) = (255, 128 * p, 64 * p)
  8119.                 c = '#%02x%02x%02x' % (int(r), int(g), int(b))
  8120.                 id = _.canvas.create_rectangle(ix, ry + 2, ix + 6, ry + rh - 2, width = 0, fill = c, outline = c)
  8121.                 ix = ix + 8
  8122.                 p = max(0.0, p - 0.1)
  8123.             
  8124.  
  8125.         
  8126.         def plog(_, gamename, gamenumber, date, status, gameid = -1, won = -1):
  8127.             if gameid > 0:
  8128.                 if gamenumber[0:1] <= gamenumber[0:1]:
  8129.                     pass
  8130.                 elif gamenumber[0:1] <= '9':
  8131.                     _.gameid = gameid
  8132.                     _.gamenumber = gamenumber
  8133.                 
  8134.             _.p('%-25s %-20s  %17s  %s\n' % (gamename, gamenumber, date, status))
  8135.             _.gameid = None
  8136.             _.gamenumber = None
  8137.  
  8138.  
  8139.  
  8140.  
  8141. class FullLog_StatsDialog(AllGames_StatsDialog):
  8142.     YVIEW = 1
  8143.     
  8144.     def fillCanvas(_, player, header):
  8145.         a = PysolStatsFormatter(_.app)
  8146.         writer = _.CanvasWriter(_.canvas, _.font, _.CHAR_H)
  8147.         if not a.writeFullLog(writer, player, header):
  8148.             writer.p('No log entries for ' + str(player) + '\n')
  8149.         
  8150.         destruct(a)
  8151.  
  8152.     
  8153.     def initKw(_, kw):
  8154.         kw = KwStruct(kw, strings = ('OK', ('Session log...', 104), ('Save to file', 203)), default = 0, font = getFont('canvas_fixed'), c_width = 76 * _.CHAR_W)
  8155.         return AllGames_StatsDialog.initKw(_, kw)
  8156.  
  8157.  
  8158.  
  8159. class SessionLog_StatsDialog(FullLog_StatsDialog):
  8160.     
  8161.     def fillCanvas(_, player, header):
  8162.         a = PysolStatsFormatter(_.app)
  8163.         writer = _.CanvasWriter(_.canvas, _.font, _.CHAR_H)
  8164.         if not a.writeSessionLog(writer, player, header):
  8165.             writer.p('No current session log entries for ' + str(player) + '\n')
  8166.         
  8167.         destruct(a)
  8168.  
  8169.     
  8170.     def initKw(_, kw):
  8171.         kw = KwStruct(kw, strings = ('OK', ('Full log...', 103), ('Save to file', 204)), default = 0)
  8172.         return FullLog_StatsDialog.initKw(_, kw)
  8173.  
  8174.  
  8175.  
  8176. class AbstractCard:
  8177.     
  8178.     def __init__(_, id, deck, suit, rank, game, x = 0, y = 0):
  8179.         _.id = id
  8180.         _.deck = deck
  8181.         _.suit = suit
  8182.         _.color = suit / 2
  8183.         _.rank = rank
  8184.         _.x = x
  8185.         _.y = y
  8186.         _.item = None
  8187.         _.face_up = 0
  8188.         _.hide_stack = None
  8189.         _.hide_x = _.hide_y = 0
  8190.  
  8191.     
  8192.     def __str__(_):
  8193.         return 'Card(%d, %d, %d, %d)' % (_.id, _.deck, _.suit, _.rank)
  8194.  
  8195.     
  8196.     def isHidden(_):
  8197.         return _.hide_stack is not None
  8198.  
  8199.     
  8200.     def moveTo(_, x, y):
  8201.         _.moveBy((x - _.x) + _.hide_x, (y - _.y) + _.hide_y)
  8202.  
  8203.     
  8204.     def moveBy(_, dx, dy):
  8205.         (dx, dy) = (int(dx), int(dy))
  8206.         if dx or dy:
  8207.             _.x = _.x + dx
  8208.             _.y = _.y + dy
  8209.             _.item.move(dx, dy)
  8210.         
  8211.  
  8212.     
  8213.     def tkraise(_, unhide = 1):
  8214.         if unhide:
  8215.             _.unhide()
  8216.         
  8217.         _.item.tkraise()
  8218.  
  8219.     
  8220.     def hide(_, stack):
  8221.         pass
  8222.  
  8223.     
  8224.     def unhide(_):
  8225.         pass
  8226.  
  8227.     
  8228.     def setSelected(_, s, group = None):
  8229.         pass
  8230.  
  8231.     
  8232.     def showFace(_, unhide = 1):
  8233.         raise SubclassResponsibility
  8234.  
  8235.     
  8236.     def showBack(_, unhide = 1):
  8237.         raise SubclassResponsibility
  8238.  
  8239.     
  8240.     def updateCardBackground(_, image):
  8241.         raise SubclassResponsibility
  8242.  
  8243.  
  8244.  
  8245. class _HideableCard_1(AbstractCard):
  8246.     
  8247.     def hide(_, stack):
  8248.         if stack is _.hide_stack:
  8249.             return None
  8250.         
  8251.         if _.hide_stack:
  8252.             (hx, hy) = (stack.hide_x - _.hide_x, stack.hide_y - _.hide_y)
  8253.         else:
  8254.             (hx, hy) = (stack.hide_x, stack.hide_y)
  8255.         item = _.item
  8256.         item.canvas.tk.call(item.canvas._w, 'move', item.id, hx, hy)
  8257.         (_.hide_x, _.hide_y) = (stack.hide_x, stack.hide_y)
  8258.         _.hide_stack = stack
  8259.  
  8260.     
  8261.     def unhide(_):
  8262.         if _.hide_stack is None:
  8263.             return 0
  8264.         
  8265.         item = _.item
  8266.         item.canvas.tk.call(item.canvas._w, 'move', item.id, -(_.hide_x), -(_.hide_y))
  8267.         (_.hide_x, _.hide_y) = (0, 0)
  8268.         _.hide_stack = None
  8269.         return 1
  8270.  
  8271.  
  8272.  
  8273. class _HideableCard_2(AbstractCard):
  8274.     
  8275.     def hide(_, stack):
  8276.         if stack is _.hide_stack:
  8277.             return None
  8278.         
  8279.         _.item.config(state = 'hidden')
  8280.         _.hide_stack = stack
  8281.  
  8282.     
  8283.     def unhide(_):
  8284.         if _.hide_stack is None:
  8285.             return 0
  8286.         
  8287.         _.item.config(state = 'normal')
  8288.         _.hide_stack = None
  8289.         return 1
  8290.  
  8291.  
  8292. _HideableCard = _HideableCard_1
  8293. if 1 and tkversion >= (8, 3, 0, 0):
  8294.     _HideableCard = _HideableCard_2
  8295.  
  8296.  
  8297. class _OneImageCard(_HideableCard):
  8298.     
  8299.     def __init__(_, id, deck, suit, rank, game, x = 0, y = 0):
  8300.         _HideableCard.__init__(_, id, deck, suit, rank, game, x = x, y = y)
  8301.         _._face_image = game.getCardFaceImage(deck, suit, rank)
  8302.         _._back_image = game.getCardBackImage(deck, suit, rank)
  8303.         _._active_image = _._back_image
  8304.         _.item = MfxCanvasImage(game.canvas, _.x, _.y, image = _._active_image, anchor = 'nw')
  8305.  
  8306.     
  8307.     def _setImage(_, image):
  8308.         if image is not _._active_image:
  8309.             _.item.config(image = image)
  8310.             _._active_image = image
  8311.         
  8312.  
  8313.     
  8314.     def showFace(_, unhide = 1):
  8315.         if not (_.face_up):
  8316.             _._setImage(image = _._face_image)
  8317.             _.tkraise(unhide)
  8318.             _.face_up = 1
  8319.         
  8320.  
  8321.     
  8322.     def showBack(_, unhide = 1):
  8323.         if _.face_up:
  8324.             _._setImage(image = _._back_image)
  8325.             _.tkraise(unhide)
  8326.             _.face_up = 0
  8327.         
  8328.  
  8329.     
  8330.     def updateCardBackground(_, image):
  8331.         _._back_image = image
  8332.         if not (_.face_up):
  8333.             _._setImage(image = image)
  8334.         
  8335.  
  8336.     
  8337.     def moveBy(_, dx, dy):
  8338.         (dx, dy) = (int(dx), int(dy))
  8339.         _.x = _.x + dx
  8340.         _.y = _.y + dy
  8341.         item = _.item
  8342.         item.canvas.tk.call(item.canvas._w, 'move', item.id, dx, dy)
  8343.  
  8344.  
  8345.  
  8346. class _OneImageCardWithHideByConfig(_OneImageCard):
  8347.     
  8348.     def hide(_, stack):
  8349.         if stack is _.hide_stack:
  8350.             return None
  8351.         
  8352.         _._setImage(image = None)
  8353.         _.hide_stack = stack
  8354.  
  8355.     
  8356.     def unhide(_):
  8357.         if _.hide_stack is None:
  8358.             return 0
  8359.         
  8360.         if _.face_up:
  8361.             _._setImage(image = _._face_image)
  8362.         else:
  8363.             _._setImage(image = _._back_image)
  8364.         _.hide_stack = None
  8365.         return 1
  8366.  
  8367.     
  8368.     def showFace(_, unhide = 1):
  8369.         if not (_.face_up):
  8370.             if unhide:
  8371.                 _._setImage(image = _._face_image)
  8372.             
  8373.             _.item.tkraise()
  8374.             _.face_up = 1
  8375.         
  8376.  
  8377.     
  8378.     def showBack(_, unhide = 1):
  8379.         if _.face_up:
  8380.             if unhide:
  8381.                 _._setImage(image = _._back_image)
  8382.             
  8383.             _.item.tkraise()
  8384.             _.face_up = 0
  8385.         
  8386.  
  8387.     
  8388.     def updateCardBackground(_, image):
  8389.         _._back_image = image
  8390.         if not (_.face_up) and not (_.hide_stack):
  8391.             _._setImage(image = image)
  8392.         
  8393.  
  8394.  
  8395.  
  8396. class _TwoImageCard(_HideableCard):
  8397.     
  8398.     def __init__(_, id, deck, suit, rank, game, x = 0, y = 0):
  8399.         _HideableCard.__init__(_, id, deck, suit, rank, game, x = x, y = y)
  8400.         _.item = MfxCanvasGroup(game.canvas)
  8401.         _._TwoImageCard__face = MfxCanvasImage(game.canvas, _.x, _.y, image = game.getCardFaceImage(deck, suit, rank), anchor = 'nw')
  8402.         _._TwoImageCard__back = MfxCanvasImage(game.canvas, _.x, _.y, image = game.getCardBackImage(deck, suit, rank), anchor = 'nw')
  8403.         _._TwoImageCard__face.addtag(_.item)
  8404.         _._TwoImageCard__back.addtag(_.item)
  8405.  
  8406.     
  8407.     def showFace(_, unhide = 1):
  8408.         if not (_.face_up):
  8409.             if TK_DASH_PATCH:
  8410.                 _._TwoImageCard__back.config(state = 'hidden')
  8411.                 _._TwoImageCard__face.config(state = 'normal')
  8412.             
  8413.             _._TwoImageCard__face.tkraise()
  8414.             _.tkraise(unhide)
  8415.             _.face_up = 1
  8416.         
  8417.  
  8418.     
  8419.     def showBack(_, unhide = 1):
  8420.         if _.face_up:
  8421.             if TK_DASH_PATCH:
  8422.                 _._TwoImageCard__face.config(state = 'hidden')
  8423.                 _._TwoImageCard__back.config(state = 'normal')
  8424.             
  8425.             _._TwoImageCard__back.tkraise()
  8426.             _.tkraise(unhide)
  8427.             _.face_up = 0
  8428.         
  8429.  
  8430.     
  8431.     def updateCardBackground(_, image):
  8432.         _._TwoImageCard__back.config(image = image)
  8433.  
  8434.  
  8435.  
  8436. class _TwoImageCardWithHideItem(_HideableCard):
  8437.     
  8438.     def __init__(_, id, deck, suit, rank, game, x = 0, y = 0):
  8439.         _HideableCard.__init__(_, id, deck, suit, rank, game, x = x, y = y)
  8440.         _.item = MfxCanvasGroup(game.canvas)
  8441.         _._TwoImageCardWithHideItem__face = MfxCanvasImage(game.canvas, _.x, _.y + 11000, image = game.getCardFaceImage(deck, suit, rank), anchor = 'nw')
  8442.         _._TwoImageCardWithHideItem__back = MfxCanvasImage(game.canvas, _.x, _.y, image = game.getCardBackImage(deck, suit, rank), anchor = 'nw')
  8443.         _._TwoImageCardWithHideItem__face.addtag(_.item)
  8444.         _._TwoImageCardWithHideItem__back.addtag(_.item)
  8445.  
  8446.     
  8447.     def showFace(_, unhide = 1):
  8448.         if not (_.face_up):
  8449.             _._TwoImageCardWithHideItem__back.move(0, 10000)
  8450.             _._TwoImageCardWithHideItem__face.move(0, -11000)
  8451.             _.tkraise(unhide)
  8452.             _.face_up = 1
  8453.         
  8454.  
  8455.     
  8456.     def showBack(_, unhide = 1):
  8457.         if _.face_up:
  8458.             _._TwoImageCardWithHideItem__face.move(0, 11000)
  8459.             _._TwoImageCardWithHideItem__back.move(0, -10000)
  8460.             _.tkraise(unhide)
  8461.             _.face_up = 0
  8462.         
  8463.  
  8464.     
  8465.     def updateCardBackground(_, image):
  8466.         _._TwoImageCardWithHideItem__back.config(image = image)
  8467.  
  8468.  
  8469. Card = _TwoImageCardWithHideItem
  8470. Card = _TwoImageCard
  8471. Card = _OneImageCardWithHideByConfig
  8472. Card = _OneImageCard
  8473.  
  8474. class ImagesCardback:
  8475.     
  8476.     def __init__(_, index, name, image, menu_image = None):
  8477.         if menu_image is None:
  8478.             menu_image = image
  8479.         
  8480.         _.index = index
  8481.         _.name = name
  8482.         _.image = image
  8483.         _.menu_image = menu_image
  8484.  
  8485.  
  8486.  
  8487. class Images:
  8488.     
  8489.     def __init__(_, dataloader, cs, r = 1):
  8490.         _.d = dataloader
  8491.         _.cs = cs
  8492.         _.reduced = r
  8493.         if cs is None:
  8494.             return None
  8495.         
  8496.         (_.CARDW, _.CARDH, _.CARDD) = (cs.CARDW / r, cs.CARDH / r, cs.CARDD / r)
  8497.         _.CARD_XOFFSET = 12 / r
  8498.         _.CARD_YOFFSET = cs.CARD_UP_YOFFSET
  8499.         if r > 1:
  8500.             _.CARD_YOFFSET = max(10, cs.CARD_UP_YOFFSET) / r
  8501.         
  8502.         (_.SHADOW_XOFFSET, _.SHADOW_YOFFSET) = (cs.SHADOW_XOFFSET / r, cs.SHADOW_YOFFSET / r)
  8503.         (_.CARD_DX, _.CARD_DY) = (cs.CARD_DX / r, cs.CARD_DY / r)
  8504.         _._shade_index = 0
  8505.         _._card = []
  8506.         _._back = []
  8507.         _._bottom = []
  8508.         _._letter = []
  8509.         _._shadow = []
  8510.         _._shade = []
  8511.  
  8512.     
  8513.     def destruct(_):
  8514.         pass
  8515.  
  8516.     
  8517.     def __loadCard(_, filename, check_w = 1, check_h = 1):
  8518.         f = os.path.join(_.cs.dir, filename)
  8519.         img = loadImage(file = f)
  8520.         (w, h) = (img.width(), img.height())
  8521.         if _.CARDW < 0:
  8522.             (_.CARDW, _.CARDH) = (w, h)
  8523.         elif check_w and w != _.CARDW and check_h and h != _.CARDH:
  8524.             raise Exception, 'Invalid size %dx%d of image %s' % (w, h, f)
  8525.         
  8526.         return img
  8527.  
  8528.     
  8529.     def __addBack(_, im1, name):
  8530.         r = max(_.CARDW / 40.0, _.CARDH / 60.0)
  8531.         r = max(2, int(round(r)))
  8532.         im2 = im1.subsample(r)
  8533.         _._back.append(ImagesCardback(len(_._back), name, im1, im2))
  8534.  
  8535.     
  8536.     def _createMissingImages(_):
  8537.         if not (_._back):
  8538.             im = createImage(_.CARDW, _.CARDH, fill = '#a0a0a0', outline = '#000000')
  8539.             name = ''
  8540.             _._Images__addBack(im, name)
  8541.             _.cs.backnames = tuple(_.cs.backnames) + (name,)
  8542.         
  8543.         bottom = None
  8544.         while len(_._bottom) < 7:
  8545.             if bottom is None:
  8546.                 bottom = createImage(_.CARDW, _.CARDH, fill = None, outline = '#000000')
  8547.             
  8548.             _._bottom.append(bottom)
  8549.         while len(_._letter) < 4:
  8550.             if bottom is None:
  8551.                 bottom = createImage(_.CARDW, _.CARDH, fill = None, outline = '#000000')
  8552.             
  8553.             _._letter.append(bottom)
  8554.  
  8555.     
  8556.     def load(_, app, progress = None, fast = 0):
  8557.         ext = _.cs.ext[1:]
  8558.         pstep = 0
  8559.         if progress:
  8560.             pstep = _.cs.ncards + len(_.cs.backnames) + _.cs.nbottoms + _.cs.nletters
  8561.             if not fast:
  8562.                 pstep = pstep + _.cs.nshadows + 1
  8563.             
  8564.             pstep = max(0, (80.0 - progress.percent) / pstep)
  8565.         
  8566.         for n in _.cs.getFaceCardNames():
  8567.             _._card.append(_._Images__loadCard(n + _.cs.ext))
  8568.             _._card[-1].filename = n
  8569.         
  8570.         if not __debug__ and len(_._card) == _.cs.ncards:
  8571.             raise AssertionError
  8572.         None if progress else _.cs.getFaceCardNames()
  8573.         for name in _.cs.backnames:
  8574.             
  8575.             try:
  8576.                 if name:
  8577.                     im = _._Images__loadCard(name)
  8578.                     _._Images__addBack(im, name)
  8579.             except:
  8580.                 0
  8581.                 _.cs.backnames
  8582.  
  8583.         
  8584.         for i in range(_.cs.nbottoms):
  8585.             
  8586.             try:
  8587.                 name = 'bottom%02d.%s' % (i + 1, ext)
  8588.                 _._bottom.append(_._Images__loadCard(name))
  8589.             except:
  8590.                 0
  8591.                 range(_.cs.nbottoms)
  8592.                 None if progress else _.cs.backnames
  8593.  
  8594.         
  8595.         for rank in range(_.cs.nletters):
  8596.             
  8597.             try:
  8598.                 name = 'l%02d.%s' % (rank + 1, ext)
  8599.                 _._letter.append(_._Images__loadCard(name))
  8600.             except:
  8601.                 0
  8602.                 range(_.cs.nletters)
  8603.                 None if progress else _.cs.backnames if progress else range(_.cs.nbottoms)
  8604.  
  8605.         
  8606.         for i in range(_.cs.nshadows):
  8607.             if progress:
  8608.                 progress.update(step = pstep)
  8609.             
  8610.         
  8611.         if fast:
  8612.             _._shade.append(None)
  8613.         else:
  8614.             _._shade.append(_._Images__loadCard('shade.' + ext))
  8615.         if progress:
  8616.             progress.update(step = pstep)
  8617.         
  8618.         _._createMissingImages()
  8619.         return 1
  8620.  
  8621.     
  8622.     def getFace(_, deck, suit, rank):
  8623.         index = suit * len(_.cs.ranks) + rank
  8624.         return _._card[index % _.cs.ncards]
  8625.  
  8626.     
  8627.     def getBack(_, deck, suit, rank):
  8628.         index = _.cs.backindex % len(_._back)
  8629.         return _._back[index].image
  8630.  
  8631.     
  8632.     def getTalonBottom(_):
  8633.         return _._bottom[0]
  8634.  
  8635.     
  8636.     def getReserveBottom(_):
  8637.         return _._bottom[0]
  8638.  
  8639.     
  8640.     def getSuitBottom(_, suit = -1):
  8641.         if not __debug__ and type(suit) is types.IntType:
  8642.             raise AssertionError
  8643.         if suit == -1:
  8644.             return _._bottom[1]
  8645.         
  8646.         i = 3 + suit
  8647.         if i >= len(_._bottom):
  8648.             return _._bottom[1]
  8649.         
  8650.         return _._bottom[i]
  8651.  
  8652.     
  8653.     def getBraidBottom(_):
  8654.         return _._bottom[2]
  8655.  
  8656.     
  8657.     def getLetter(_, rank):
  8658.         if __debug__:
  8659.             if rank <= rank:
  8660.                 pass
  8661.             elif not rank <= 3:
  8662.                 raise AssertionError
  8663.         if rank >= len(_._letter):
  8664.             return _._bottom[0]
  8665.         
  8666.         return _._letter[rank]
  8667.  
  8668.     
  8669.     def getShadow(_, ncards):
  8670.         if not __debug__ and ncards >= 0:
  8671.             raise AssertionError
  8672.         if ncards >= len(_._shadow):
  8673.             return None
  8674.         
  8675.         return _._shadow[ncards]
  8676.  
  8677.     
  8678.     def getShade(_):
  8679.         return _._shade[_._shade_index]
  8680.  
  8681.     
  8682.     def getCardbacks(_):
  8683.         return _._back
  8684.  
  8685.  
  8686.  
  8687. class SubsampledImages(Images):
  8688.     
  8689.     def __init__(_, images, r = 2):
  8690.         Images.__init__(_, None, images.cs, r = r)
  8691.         _._card = _._subsample(images._card, r)
  8692.         _._bottom = _._subsample(images._bottom, r)
  8693.         _._letter = _._subsample(images._letter, r)
  8694.         for _back in images._back:
  8695.             pass
  8696.         
  8697.         (CW, CH) = (_.CARDW, _.CARDH)
  8698.         for im in images._shade:
  8699.             pass
  8700.         
  8701.  
  8702.     
  8703.     def getShadow(_, ncards):
  8704.         return None
  8705.  
  8706.     
  8707.     def _subsample(_, l, r):
  8708.         s = []
  8709.         for im in l:
  8710.             pass
  8711.         
  8712.         return s
  8713.  
  8714.  
  8715.  
  8716. class AtomicMove:
  8717.     
  8718.     def do(_, game):
  8719.         _.redo(game)
  8720.  
  8721.     
  8722.     def __repr__(_):
  8723.         return str(_.__dict__)
  8724.  
  8725.     
  8726.     def __str__(_):
  8727.         return str(_.__dict__)
  8728.  
  8729.     
  8730.     def cmpForRedo(_, other):
  8731.         return -1
  8732.  
  8733.  
  8734.  
  8735. class AMoveMove(AtomicMove):
  8736.     
  8737.     def __init__(_, ncards, from_stack, to_stack, frames, shadow = -1):
  8738.         if not __debug__ and from_stack is not to_stack:
  8739.             raise AssertionError
  8740.         _.ncards = ncards
  8741.         _.from_stack_id = from_stack.id
  8742.         _.to_stack_id = to_stack.id
  8743.         _.frames = frames
  8744.         _.shadow = shadow
  8745.  
  8746.     
  8747.     def __doMove(_, game, ncards, from_stack, to_stack):
  8748.         if game.moves.state == game.S_PLAY:
  8749.             if not __debug__ and to_stack.acceptsCards(from_stack, from_stack.cards[-ncards:]):
  8750.                 raise AssertionError
  8751.         
  8752.         cards = []
  8753.         for i in range(ncards):
  8754.             card = from_stack.removeCard()
  8755.             cards.append(card)
  8756.         
  8757.         cards.reverse()
  8758.         for c in cards:
  8759.             to_stack.addCard(c)
  8760.         
  8761.  
  8762.     
  8763.     def redo(_, game):
  8764.         _._AMoveMove__doMove(game, _.ncards, game.allstacks[_.from_stack_id], game.allstacks[_.to_stack_id])
  8765.  
  8766.     
  8767.     def undo(_, game):
  8768.         _._AMoveMove__doMove(game, _.ncards, game.allstacks[_.to_stack_id], game.allstacks[_.from_stack_id])
  8769.  
  8770.     
  8771.     def cmpForRedo(_, other):
  8772.         if not cmp(_.ncards, other.ncards) and cmp(_.from_stack_id, other.from_stack_id):
  8773.             pass
  8774.         return cmp(_.to_stack_id, other.to_stack_id)
  8775.  
  8776.  
  8777.  
  8778. class AFlipMove(AtomicMove):
  8779.     
  8780.     def __init__(_, stack):
  8781.         _.stack_id = stack.id
  8782.  
  8783.     
  8784.     def __doMove(_, game, stack):
  8785.         card = stack.cards[-1]
  8786.         if card.face_up:
  8787.             card.showBack()
  8788.         else:
  8789.             card.showFace()
  8790.  
  8791.     
  8792.     def redo(_, game):
  8793.         _._AFlipMove__doMove(game, game.allstacks[_.stack_id])
  8794.  
  8795.     
  8796.     def undo(_, game):
  8797.         _._AFlipMove__doMove(game, game.allstacks[_.stack_id])
  8798.  
  8799.     
  8800.     def cmpForRedo(_, other):
  8801.         return cmp(_.stack_id, other.stack_id)
  8802.  
  8803.  
  8804.  
  8805. class ATurnStackMove(AtomicMove):
  8806.     
  8807.     def __init__(_, from_stack, to_stack, update_flags = 1):
  8808.         if not __debug__ and from_stack is not to_stack:
  8809.             raise AssertionError
  8810.         _.from_stack_id = from_stack.id
  8811.         _.to_stack_id = to_stack.id
  8812.         _.update_flags = update_flags
  8813.  
  8814.     
  8815.     def redo(_, game):
  8816.         from_stack = game.allstacks[_.from_stack_id]
  8817.         to_stack = game.allstacks[_.to_stack_id]
  8818.         if not __debug__ and len(from_stack.cards) > 0:
  8819.             raise AssertionError
  8820.         if not __debug__ and len(to_stack.cards) == 0:
  8821.             raise AssertionError
  8822.         l = len(from_stack.cards)
  8823.         for i in range(l):
  8824.             unhide = 1
  8825.             card = from_stack.removeCard(unhide = unhide, update = 0)
  8826.             if not __debug__ and card.face_up:
  8827.                 raise AssertionError
  8828.             0
  8829.             to_stack.addCard(card, unhide = unhide, update = 0)
  8830.             card.showBack(unhide = unhide)
  8831.         
  8832.         from_stack.updateText()
  8833.         to_stack.updateText()
  8834.  
  8835.     
  8836.     def undo(_, game):
  8837.         from_stack = game.allstacks[_.to_stack_id]
  8838.         to_stack = game.allstacks[_.from_stack_id]
  8839.         if not __debug__ and len(from_stack.cards) > 0:
  8840.             raise AssertionError
  8841.         if not __debug__ and len(to_stack.cards) == 0:
  8842.             raise AssertionError
  8843.         l = len(from_stack.cards)
  8844.         for i in range(l):
  8845.             unhide = 1
  8846.             card = from_stack.removeCard(unhide = unhide, update = 0)
  8847.             if not __debug__ and not (card.face_up):
  8848.                 raise AssertionError
  8849.             0
  8850.             card.showFace(unhide = unhide)
  8851.             to_stack.addCard(card, unhide = unhide, update = 0)
  8852.         
  8853.         from_stack.updateText()
  8854.         to_stack.updateText()
  8855.  
  8856.     
  8857.     def cmpForRedo(_, other):
  8858.         if not cmp(_.from_stack_id, other.from_stack_id) and cmp(_.to_stack_id, other.to_stack_id):
  8859.             pass
  8860.         return cmp(_.update_flags, other.update_flags)
  8861.  
  8862.  
  8863.  
  8864. class NEW_ATurnStackMove(AtomicMove):
  8865.     
  8866.     def __init__(_, from_stack, to_stack, update_flags = 1):
  8867.         if not __debug__ and from_stack is not to_stack:
  8868.             raise AssertionError
  8869.         _.from_stack_id = from_stack.id
  8870.         _.to_stack_id = to_stack.id
  8871.         _.update_flags = update_flags
  8872.  
  8873.     
  8874.     def __doMove(_, from_stack, to_stack, show_face):
  8875.         if not __debug__ and len(from_stack.cards) > 0:
  8876.             raise AssertionError
  8877.         if not __debug__ and len(to_stack.cards) == 0:
  8878.             raise AssertionError
  8879.         for card in from_stack.cards:
  8880.             card.item.dtag(from_stack.group)
  8881.             card.item.addtag(to_stack.group)
  8882.             if show_face:
  8883.                 if not __debug__ and not (card.face_up):
  8884.                     raise AssertionError
  8885.                 0
  8886.                 card.showFace(unhide = 0)
  8887.             elif not __debug__ and card.face_up:
  8888.                 raise AssertionError
  8889.             card.showBack(unhide = 0)
  8890.         
  8891.         to_stack.cards = from_stack.cards
  8892.         from_stack.cards = []
  8893.         from_stack.refreshView()
  8894.         from_stack.updateText()
  8895.         to_stack.refreshView()
  8896.         to_stack.updateText()
  8897.  
  8898.     
  8899.     def redo(_, game):
  8900.         from_stack = game.allstacks[_.from_stack_id]
  8901.         to_stack = game.allstacks[_.to_stack_id]
  8902.         if _.update_flags & 1:
  8903.             if not __debug__ and to_stack is game.s.talon:
  8904.                 raise AssertionError
  8905.             if __debug__:
  8906.                 if not to_stack.round < to_stack.max_rounds or to_stack.max_rounds < 0:
  8907.                     raise AssertionError
  8908.             to_stack.round = to_stack.round + 1
  8909.         
  8910.         _._NEW_ATurnStackMove__doMove(from_stack, to_stack, 0)
  8911.  
  8912.     
  8913.     def undo(_, game):
  8914.         from_stack = game.allstacks[_.from_stack_id]
  8915.         to_stack = game.allstacks[_.to_stack_id]
  8916.         if _.update_flags & 1:
  8917.             if not __debug__ and to_stack is game.s.talon:
  8918.                 raise AssertionError
  8919.             if not __debug__ and to_stack.round > 1:
  8920.                 raise AssertionError
  8921.             to_stack.round = to_stack.round - 1
  8922.         
  8923.         _._NEW_ATurnStackMove__doMove(to_stack, from_stack, 1)
  8924.  
  8925.     
  8926.     def cmpForRedo(_, other):
  8927.         if not cmp(_.from_stack_id, other.from_stack_id) and cmp(_.to_stack_id, other.to_stack_id):
  8928.             pass
  8929.         return cmp(_.update_flags, other.update_flags)
  8930.  
  8931.  
  8932.  
  8933. class AUpdateStackMove(AtomicMove):
  8934.     
  8935.     def __init__(_, stack, flags):
  8936.         _.stack_id = stack.id
  8937.         _.flags = flags
  8938.  
  8939.     
  8940.     def __doMove(_, game, stack, undo):
  8941.         if _.flags & 64:
  8942.             stack.updateModel(undo, _.flags)
  8943.         elif _.flags & 16:
  8944.             stack.updateText()
  8945.         
  8946.         if _.flags & 32:
  8947.             stack.refreshView()
  8948.         
  8949.  
  8950.     
  8951.     def redo(_, game):
  8952.         if _.flags & 3 in (1, 3):
  8953.             _._AUpdateStackMove__doMove(game, game.allstacks[_.stack_id], 0)
  8954.         
  8955.  
  8956.     
  8957.     def undo(_, game):
  8958.         if _.flags & 3 in (2, 3):
  8959.             _._AUpdateStackMove__doMove(game, game.allstacks[_.stack_id], 1)
  8960.         
  8961.  
  8962.     
  8963.     def cmpForRedo(_, other):
  8964.         if not cmp(_.stack_id, other.stack_id):
  8965.             pass
  8966.         return cmp(_.flags, other.flags)
  8967.  
  8968.  
  8969. AUpdateStackModelMove = AUpdateStackMove
  8970. AUpdateStackViewMove = AUpdateStackMove
  8971.  
  8972. class ANextRoundMove(AtomicMove):
  8973.     
  8974.     def __init__(_, stack):
  8975.         _.stack_id = stack.id
  8976.  
  8977.     
  8978.     def redo(_, game):
  8979.         stack = game.allstacks[_.stack_id]
  8980.         if not __debug__ and stack is game.s.talon:
  8981.             raise AssertionError
  8982.         if __debug__:
  8983.             if not stack.round < stack.max_rounds or stack.max_rounds < 0:
  8984.                 raise AssertionError
  8985.         stack.round = stack.round + 1
  8986.         stack.updateText()
  8987.  
  8988.     
  8989.     def undo(_, game):
  8990.         stack = game.allstacks[_.stack_id]
  8991.         if not __debug__ and stack is game.s.talon:
  8992.             raise AssertionError
  8993.         if not __debug__ and stack.round > 1:
  8994.             raise AssertionError
  8995.         stack.round = stack.round - 1
  8996.         stack.updateText()
  8997.  
  8998.     
  8999.     def cmpForRedo(_, other):
  9000.         return cmp(_.stack_id, other.stack_id)
  9001.  
  9002.  
  9003.  
  9004. class ASaveSeedMove(AtomicMove):
  9005.     
  9006.     def __init__(_, game):
  9007.         _.seed = game.random.getSeed()
  9008.  
  9009.     
  9010.     def redo(_, game):
  9011.         game.random.setSeed(_.seed)
  9012.  
  9013.     
  9014.     def undo(_, game):
  9015.         game.random.setSeed(_.seed)
  9016.  
  9017.     
  9018.     def cmpForRedo(_, other):
  9019.         return cmp(_.seed, other.seed)
  9020.  
  9021.  
  9022.  
  9023. class AShuffleStackMove(AtomicMove):
  9024.     
  9025.     def __init__(_, stack, game):
  9026.         _.stack_id = stack.id
  9027.         _.card_ids = tuple(map((lambda c: c.id), stack.cards))
  9028.         _.seed = game.random.getSeed()
  9029.  
  9030.     
  9031.     def redo(_, game):
  9032.         stack = game.allstacks[_.stack_id]
  9033.         if not __debug__ and stack is game.s.talon:
  9034.             raise AssertionError
  9035.         if not __debug__ and _.card_ids == tuple(map((lambda c: c.id), stack.cards)):
  9036.             raise AssertionError
  9037.         game.random.setSeed(_.seed)
  9038.         seq = stack.cards
  9039.         n = len(seq) - 1
  9040.         while n > 0:
  9041.             j = game.random.randint(0, n)
  9042.             (seq[n], seq[j]) = (seq[j], seq[n])
  9043.             n = n - 1
  9044.         stack.refreshView()
  9045.  
  9046.     
  9047.     def undo(_, game):
  9048.         stack = game.allstacks[_.stack_id]
  9049.         cards = []
  9050.         for id in _.card_ids:
  9051.             c = game.cards[id]
  9052.             if not __debug__ and c.id == id:
  9053.                 raise AssertionError
  9054.             0
  9055.             cards.append(c)
  9056.         
  9057.         stack.cards = cards
  9058.         game.random.setSeed(_.seed)
  9059.         stack.refreshView()
  9060.  
  9061.     
  9062.     def cmpForRedo(_, other):
  9063.         if not cmp(_.stack_id, other.stack_id) and cmp(_.card_ids, other.card_ids):
  9064.             pass
  9065.         return cmp(_.seed, other.seed)
  9066.  
  9067.  
  9068.  
  9069. def cardsFaceUp(cards):
  9070.     if not cards:
  9071.         return 0
  9072.     
  9073.     for c in cards:
  9074.         pass
  9075.     
  9076.     return 1
  9077.  
  9078.  
  9079. def cardsFaceDown(cards):
  9080.     if not cards:
  9081.         return 0
  9082.     
  9083.     for c in cards:
  9084.         pass
  9085.     
  9086.     return 1
  9087.  
  9088.  
  9089. def isRankSequence(cards, mod = 8192, dir = -1):
  9090.     if not cardsFaceUp(cards):
  9091.         return 0
  9092.     
  9093.     c1 = cards[0]
  9094.     for c2 in cards[1:]:
  9095.         c1 = c2
  9096.     
  9097.     return 1
  9098.  
  9099.  
  9100. def isAlternateColorSequence(cards, mod = 8192, dir = -1):
  9101.     if not cardsFaceUp(cards):
  9102.         return 0
  9103.     
  9104.     c1 = cards[0]
  9105.     for c2 in cards[1:]:
  9106.         c1 = c2
  9107.     
  9108.     return 1
  9109.  
  9110.  
  9111. def isSameColorSequence(cards, mod = 8192, dir = -1):
  9112.     if not cardsFaceUp(cards):
  9113.         return 0
  9114.     
  9115.     c1 = cards[0]
  9116.     for c2 in cards[1:]:
  9117.         c1 = c2
  9118.     
  9119.     return 1
  9120.  
  9121.  
  9122. def isSameSuitSequence(cards, mod = 8192, dir = -1):
  9123.     if not cardsFaceUp(cards):
  9124.         return 0
  9125.     
  9126.     c1 = cards[0]
  9127.     for c2 in cards[1:]:
  9128.         c1 = c2
  9129.     
  9130.     return 1
  9131.  
  9132.  
  9133. def isAnySuitButOwnSequence(cards, mod = 8192, dir = -1):
  9134.     if not cardsFaceUp(cards):
  9135.         return 0
  9136.     
  9137.     c1 = cards[0]
  9138.     for c2 in cards[1:]:
  9139.         c1 = c2
  9140.     
  9141.     return 1
  9142.  
  9143.  
  9144. def getNumberOfFreeStacks(stacks):
  9145.     return len(filter((lambda s: not (s.cards)), stacks))
  9146.  
  9147.  
  9148. def getPileFromStacks(stacks, reverse = 0):
  9149.     cards = []
  9150.     for s in stacks:
  9151.         cards.append(s.cards[-1])
  9152.     
  9153.     if reverse:
  9154.         cards.reverse()
  9155.     
  9156.     return cards
  9157.  
  9158.  
  9159. class Stack:
  9160.     
  9161.     def __init__(_, x, y, game, cap = { }):
  9162.         id = len(game.allstacks)
  9163.         game.allstacks.append(_)
  9164.         x = int(round(x))
  9165.         y = int(round(y))
  9166.         mapkey = (x, y)
  9167.         game.stackmap[mapkey] = id
  9168.         (model, view, controller) = (_, _, _)
  9169.         model.id = id
  9170.         model.game = game
  9171.         model.cards = []
  9172.         model.cap = Struct(suit = -1, color = -1, rank = -1, base_suit = -1, base_color = -1, base_rank = -1, dir = 0, mod = 8192, max_move = 0, max_accept = 0, max_cards = 999999, min_move = 1, min_accept = 1, min_cards = 0)
  9173.         model.cap.update(cap)
  9174.         if not __debug__ and type(model.cap.suit) is types.IntType:
  9175.             raise AssertionError
  9176.         if not __debug__ and type(model.cap.color) is types.IntType:
  9177.             raise AssertionError
  9178.         if not __debug__ and type(model.cap.rank) is types.IntType:
  9179.             raise AssertionError
  9180.         if not __debug__ and type(model.cap.base_suit) is types.IntType:
  9181.             raise AssertionError
  9182.         if not __debug__ and type(model.cap.base_color) is types.IntType:
  9183.             raise AssertionError
  9184.         if not __debug__ and type(model.cap.base_rank) is types.IntType:
  9185.             raise AssertionError
  9186.         view.x = x
  9187.         view.y = y
  9188.         view.canvas = game.canvas
  9189.         view.CARD_XOFFSET = 0
  9190.         view.CARD_YOFFSET = 0
  9191.         view.group = MfxCanvasGroup(view.canvas)
  9192.         view.images = Struct(bottom = None, redeal = None, redeal_img = None)
  9193.         view.items = Struct(bottom = None)
  9194.         view.texts = Struct(ncards = None, rounds = None, redeal = None, redeal_str = None, misc = None)
  9195.         view.top_bottom = None
  9196.         if view.x >= -100:
  9197.             pass
  9198.         view.is_visible = view.y >= -100
  9199.         view.is_open = -1
  9200.         view.can_hide_cards = -1
  9201.         view.max_shadow_cards = -1
  9202.  
  9203.     
  9204.     def destruct(_):
  9205.         unbind_destroy(_.group)
  9206.  
  9207.     
  9208.     def prepareStack(_):
  9209.         _.prepareView()
  9210.         if _.is_visible:
  9211.             _.initBindings()
  9212.         
  9213.  
  9214.     
  9215.     def initBindings(_):
  9216.         group = _.group
  9217.         bind(group, '<1>', _._Stack__clickEventHandler)
  9218.         bind(group, '<B1-Motion>', _._Stack__motionEventHandler)
  9219.         bind(group, '<ButtonRelease-1>', _._Stack__releaseEventHandler)
  9220.         bind(group, '<Control-1>', _._Stack__controlclickEventHandler)
  9221.         bind(group, '<Shift-1>', _._Stack__shiftclickEventHandler)
  9222.         bind(group, '<Double-1>', _._Stack__doubleclickEventHandler)
  9223.         bind(group, '<3>', _._Stack__rightclickEventHandler)
  9224.         bind(group, '<2>', _._Stack__middleclickEventHandler)
  9225.         bind(group, '<Control-3>', _._Stack__middleclickEventHandler)
  9226.  
  9227.     
  9228.     def prepareView(_):
  9229.         (ox, oy) = (_.CARD_XOFFSET, _.CARD_YOFFSET)
  9230.         if type(ox) is types.IntType:
  9231.             _.CARD_XOFFSET = (ox,)
  9232.         else:
  9233.             _.CARD_XOFFSET = tuple(map(int, map(round, ox)))
  9234.         if type(oy) is types.IntType:
  9235.             _.CARD_YOFFSET = (oy,)
  9236.         else:
  9237.             _.CARD_YOFFSET = tuple(map(int, map(round, oy)))
  9238.         if _.can_hide_cards < 0:
  9239.             _.can_hide_cards = _.is_visible
  9240.             if _.cap.max_cards < 3:
  9241.                 _.can_hide_cards = 0
  9242.             elif filter(None, _.CARD_XOFFSET):
  9243.                 _.can_hide_cards = 0
  9244.             elif filter(None, _.CARD_YOFFSET):
  9245.                 _.can_hide_cards = 0
  9246.             elif _.canvas.preview:
  9247.                 _.can_hide_cards = 0
  9248.             
  9249.         
  9250.         if _.can_hide_cards:
  9251.             (CW, CH) = (_.game.app.images.CARDW, _.game.app.images.CARDH)
  9252.             cx = _.x + CW / 2
  9253.             cy = _.y + CH / 2
  9254.             if cy < 3 * CH / 2:
  9255.                 (_.hide_x, _.hide_y) = (0, -10000)
  9256.             elif cx < 3 * CW / 2:
  9257.                 (_.hide_x, _.hide_y) = (-10000, 0)
  9258.             elif cy > _.game.height - 3 * CH / 2:
  9259.                 (_.hide_x, _.hide_y) = (0, 10000)
  9260.             else:
  9261.                 (_.hide_x, _.hide_y) = (10000, 0)
  9262.         
  9263.         if _.is_open < 0:
  9264.             if not _.is_visible and abs(_.CARD_XOFFSET[0]) >= 5:
  9265.                 pass
  9266.             _.is_open = abs(_.CARD_YOFFSET[0]) >= 5
  9267.         
  9268.         if _.max_shadow_cards < 0:
  9269.             _.max_shadow_cards = 999999
  9270.             if abs(_.CARD_YOFFSET[0]) != _.game.app.images.CARD_YOFFSET:
  9271.                 _.max_shadow_cards = 1
  9272.             
  9273.         
  9274.         if _.is_visible:
  9275.             _.prepareBottom()
  9276.         
  9277.  
  9278.     
  9279.     def prepareBottom(_):
  9280.         if __debug__:
  9281.             if not _.is_visible and _.images.bottom is None:
  9282.                 raise AssertionError
  9283.         img = _.getBottomImage()
  9284.         if img is not None:
  9285.             _.images.bottom = MfxCanvasImage(_.canvas, _.x, _.y, image = img, anchor = ANCHOR_NW)
  9286.             _.images.bottom.addtag(_.group)
  9287.             _.top_bottom = _.images.bottom
  9288.         
  9289.  
  9290.     
  9291.     def prepareInvisibleBottom(_):
  9292.         if __debug__:
  9293.             if not _.is_visible and _.items.bottom is None:
  9294.                 raise AssertionError
  9295.         images = _.game.app.images
  9296.         _.items.bottom = MfxCanvasRectangle(_.canvas, _.x, _.y, _.x + images.CARDW, _.y + images.CARDH, fill = '', outline = '', width = 0)
  9297.         _.items.bottom.addtag(_.group)
  9298.         _.top_bottom = _.items.bottom
  9299.  
  9300.     
  9301.     def assertStack(_):
  9302.         if not __debug__ and _.cap.min_move > 0:
  9303.             raise AssertionError
  9304.         if not __debug__ and _.cap.min_accept > 0:
  9305.             raise AssertionError
  9306.         if not __debug__ and not hasattr(_, 'suit'):
  9307.             raise AssertionError
  9308.  
  9309.     
  9310.     def addCard(_, card, unhide = 1, update = 1):
  9311.         (model, view) = (_, _)
  9312.         model.cards.append(card)
  9313.         card.tkraise(unhide = unhide)
  9314.         if view.can_hide_cards and len(model.cards) >= 3:
  9315.             model.cards[-3].hide(_)
  9316.         
  9317.         card.item.addtag(view.group)
  9318.         view._position(card)
  9319.         if update:
  9320.             view.updateText()
  9321.         
  9322.         return card
  9323.  
  9324.     
  9325.     def removeCard(_, card = None, unhide = 1, update = 1):
  9326.         (model, view) = (_, _)
  9327.         if not __debug__ and len(model.cards) > 0:
  9328.             raise AssertionError
  9329.         if card is None:
  9330.             card = model.cards[-1]
  9331.             card.item.dtag(view.group)
  9332.             if unhide and _.can_hide_cards:
  9333.                 card.unhide()
  9334.                 if len(_.cards) >= 3:
  9335.                     model.cards[-3].unhide()
  9336.                 
  9337.             
  9338.             del model.cards[-1]
  9339.         else:
  9340.             card.item.dtag(view.group)
  9341.             if unhide and view.can_hide_cards:
  9342.                 card.unhide()
  9343.                 if len(model.cards) >= 3:
  9344.                     if card is model.cards[-1] or model is _.cards[-2]:
  9345.                         model.cards[-3].unhide()
  9346.                     
  9347.                 
  9348.             
  9349.             model.cards.remove(card)
  9350.         if update:
  9351.             view.updateText()
  9352.         
  9353.         return card
  9354.  
  9355.     
  9356.     def getCard(_):
  9357.         if _.cards:
  9358.             return _.cards[-1]
  9359.         
  9360.         return None
  9361.  
  9362.     
  9363.     def getPile(_):
  9364.         if _.cap.max_move > 0:
  9365.             cards = _.cards[-(_.cap.max_move):]
  9366.             while len(cards) >= _.cap.min_move:
  9367.                 if _.canMoveCards(cards):
  9368.                     return cards
  9369.                 
  9370.                 del cards[0]
  9371.         
  9372.         return None
  9373.  
  9374.     
  9375.     def _position(_, card):
  9376.         (x, y) = _.getPositionFor(card)
  9377.         card.moveTo(x, y)
  9378.  
  9379.     
  9380.     def _findCard(_, event):
  9381.         (model, view) = (_, _)
  9382.         if event is not None and model.cards:
  9383.             return view.canvas.findCard(_, event)
  9384.         
  9385.         return -1
  9386.  
  9387.     
  9388.     def _findCardXY(_, x, y, cards = None):
  9389.         (model, view) = (_, _)
  9390.         if cards is None:
  9391.             cards = model.cards
  9392.         
  9393.         images = _.game.app.images
  9394.         index = -1
  9395.         for i in range(len(cards)):
  9396.             c = cards[i]
  9397.             r = (c.x, c.y, c.x + images.CARDW, c.y + images.CARDH)
  9398.         
  9399.         return index
  9400.  
  9401.     
  9402.     def updateModel(_, undo, flags):
  9403.         pass
  9404.  
  9405.     
  9406.     def copyModel(_, clone):
  9407.         clone.id = _.id
  9408.         clone.game = _.game
  9409.         clone.cap = _.cap
  9410.  
  9411.     
  9412.     def getRankDir(_, cards = None):
  9413.         if cards is None:
  9414.             cards = _.cards[-2:]
  9415.         
  9416.         if len(cards) < 2:
  9417.             return 0
  9418.         
  9419.         dir = (cards[-1].rank - cards[-2].rank) % _.cap.mod
  9420.         if dir > _.cap.mod / 2:
  9421.             return dir - _.cap.mod
  9422.         
  9423.         return dir
  9424.  
  9425.     
  9426.     def basicIsBlocked(_):
  9427.         return 0
  9428.  
  9429.     
  9430.     def basicAcceptsCards(_, from_stack, cards):
  9431.         if from_stack is _ or _.basicIsBlocked():
  9432.             return 0
  9433.         
  9434.         cap = _.cap
  9435.         l = len(cards)
  9436.         if l < cap.min_accept or l > cap.max_accept:
  9437.             return 0
  9438.         
  9439.         l = l + len(_.cards)
  9440.         if l > cap.max_cards:
  9441.             return 0
  9442.         
  9443.         for c in cards:
  9444.             if cap.suit >= 0 and c.suit != cap.suit:
  9445.                 return 0
  9446.             
  9447.             if cap.color >= 0 and c.color != cap.color:
  9448.                 return 0
  9449.             
  9450.             if cap.rank >= 0 and c.rank != cap.rank:
  9451.                 return 0
  9452.             
  9453.         
  9454.         if _.cards:
  9455.             return _.cards[-1].face_up
  9456.         else:
  9457.             c = cards[0]
  9458.             if cap.base_suit >= 0 and c.suit != cap.base_suit:
  9459.                 return 0
  9460.             
  9461.             if cap.base_color >= 0 and c.color != cap.base_color:
  9462.                 return 0
  9463.             
  9464.             if cap.base_rank >= 0 and c.rank != cap.base_rank:
  9465.                 return 0
  9466.             
  9467.             return 1
  9468.  
  9469.     
  9470.     def basicCanMoveCards(_, cards):
  9471.         if _.basicIsBlocked():
  9472.             return 0
  9473.         
  9474.         cap = _.cap
  9475.         l = len(cards)
  9476.         if l < cap.min_move or l > cap.max_move:
  9477.             return 0
  9478.         
  9479.         l = len(_.cards) - l
  9480.         if l < cap.min_cards:
  9481.             return 0
  9482.         
  9483.         return cardsFaceUp(cards)
  9484.  
  9485.     
  9486.     def acceptsCards(_, from_stack, cards):
  9487.         return 0
  9488.  
  9489.     
  9490.     def canMoveCards(_, cards):
  9491.         return 0
  9492.  
  9493.     
  9494.     def canFlipCard(_):
  9495.         return 0
  9496.  
  9497.     
  9498.     def canDropCards(_, stacks):
  9499.         return (None, 0)
  9500.  
  9501.     
  9502.     def resetGame(_):
  9503.         pass
  9504.  
  9505.     
  9506.     def __repr__(_):
  9507.         return '%s(%d)' % (_.__class__.__name__, _.id)
  9508.  
  9509.     
  9510.     def flipMove(_):
  9511.         _.game.flipMove(_)
  9512.  
  9513.     
  9514.     def moveMove(_, ncards, to_stack, frames = -1, shadow = -1):
  9515.         _.game.moveMove(ncards, _, to_stack, frames = frames, shadow = shadow)
  9516.         _.fillStack()
  9517.  
  9518.     
  9519.     def fillStack(_):
  9520.         _.game.fillStack(_)
  9521.  
  9522.     
  9523.     def playFlipMove(_, sound = 1):
  9524.         if sound:
  9525.             _.game.playSample('flip', 5)
  9526.         
  9527.         _.flipMove()
  9528.         if not _.game.checkForWin():
  9529.             _.game.autoPlay()
  9530.         
  9531.         _.game.finishMove()
  9532.  
  9533.     
  9534.     def playMoveMove(_, ncards, to_stack, frames = -1, shadow = -1, sound = 1):
  9535.         if sound:
  9536.             if to_stack in _.game.s.foundations:
  9537.                 _.game.playSample('drop', priority = 30)
  9538.             else:
  9539.                 _.game.playSample('move', priority = 10)
  9540.         
  9541.         _.moveMove(ncards, to_stack, frames = frames, shadow = shadow)
  9542.         if not _.game.checkForWin():
  9543.             if not (_ in _.game.s.foundations):
  9544.                 _.game.autoPlay()
  9545.             
  9546.         
  9547.         _.game.finishMove()
  9548.  
  9549.     
  9550.     def getBottomImage(_):
  9551.         return None
  9552.  
  9553.     
  9554.     def getPositionFor(_, card):
  9555.         (model, view) = (_, _)
  9556.         if view.can_hide_cards:
  9557.             return (view.x, view.y)
  9558.         
  9559.         (x, y) = (view.x, view.y)
  9560.         (ix, iy, lx, ly) = (0, 0, len(view.CARD_XOFFSET), len(view.CARD_YOFFSET))
  9561.         for c in model.cards:
  9562.             x = x + view.CARD_XOFFSET[ix]
  9563.             y = y + view.CARD_YOFFSET[iy]
  9564.             ix = (ix + 1) % lx
  9565.             iy = (iy + 1) % ly
  9566.         
  9567.         return (x, y)
  9568.  
  9569.     
  9570.     def refreshView(_):
  9571.         (model, view) = (_, _)
  9572.         cards = model.cards
  9573.         if not (view.is_visible) or len(cards) < 2:
  9574.             return None
  9575.         
  9576.         item = cards[0].item
  9577.         (x, y) = (view.x, view.y)
  9578.         (ix, iy, lx, ly) = (0, 0, len(view.CARD_XOFFSET), len(view.CARD_YOFFSET))
  9579.         for c in cards[1:]:
  9580.             c.item.tkraise(item)
  9581.             item = c.item
  9582.         
  9583.  
  9584.     
  9585.     def updateText(_):
  9586.         if _.game.preview > 1 or _.texts.ncards is None:
  9587.             return None
  9588.         
  9589.         t = ''
  9590.         format = '%d'
  9591.         if _.texts.ncards.text_format is not None:
  9592.             format = _.texts.ncards.text_format
  9593.             if format == '%D':
  9594.                 format = ''
  9595.                 if _.cards:
  9596.                     format = '%d'
  9597.                 
  9598.             
  9599.         
  9600.         if format:
  9601.             t = format % len(_.cards)
  9602.         
  9603.         if 0 and _.game.app.debug:
  9604.             visible = 0
  9605.             for c in _.cards:
  9606.                 if c.isHidden():
  9607.                     if not __debug__ and c.hide_stack is not None:
  9608.                         raise AssertionError
  9609.                     0
  9610.                     if __debug__:
  9611.                         if not c.hide_x != 0 or c.hide_y != 0:
  9612.                             raise AssertionError
  9613.                     _.cards
  9614.                 else:
  9615.                     visible = visible + 1
  9616.                     if not __debug__ and c.hide_stack is None:
  9617.                         raise AssertionError
  9618.                     0
  9619.                     if __debug__:
  9620.                         if not c.hide_x == 0 and c.hide_y == 0:
  9621.                             raise AssertionError
  9622.                     _.cards
  9623.             
  9624.             t = t + ' %2d' % visible
  9625.         
  9626.         _.texts.ncards.config(text = t)
  9627.  
  9628.     
  9629.     def basicShallHighlightSameRank(_, card):
  9630.         if not __debug__ and card in _.cards:
  9631.             raise AssertionError
  9632.         if not (_.is_visible) or not (card.face_up):
  9633.             return 0
  9634.         
  9635.         if card is _.cards[-1]:
  9636.             return 1
  9637.         
  9638.         return _.is_open
  9639.  
  9640.     
  9641.     def basicShallHighlightMatch(_, card):
  9642.         return _.basicShallHighlightSameRank(card)
  9643.  
  9644.     
  9645.     def highlightSameRank(_, event):
  9646.         i = _._findCard(event)
  9647.         if i < 0:
  9648.             return 0
  9649.         
  9650.         card = _.cards[i]
  9651.         if not _.basicShallHighlightSameRank(card):
  9652.             return 0
  9653.         
  9654.         col = _.game.app.opt.highlight_samerank_colors
  9655.         info = [
  9656.             (_, card, card, col[1])]
  9657.         for s in _.game.allstacks:
  9658.             for c in s.cards:
  9659.                 if s.basicShallHighlightSameRank(c):
  9660.                     info.append((s, c, c, col[3]))
  9661.                 
  9662.             
  9663.         
  9664.         _.game.stats.highlight_samerank = _.game.stats.highlight_samerank + 1
  9665.         return _.game._highlightCards(info, _.game.app.opt.highlight_samerank_sleep)
  9666.  
  9667.     
  9668.     def highlightMatchingCards(_, event):
  9669.         i = _._findCard(event)
  9670.         if i < 0:
  9671.             return 0
  9672.         
  9673.         card = _.cards[i]
  9674.         if not _.basicShallHighlightMatch(card):
  9675.             return 0
  9676.         
  9677.         col = _.game.app.opt.highlight_cards_colors
  9678.         c1 = c2 = card
  9679.         info = []
  9680.         found = 0
  9681.         for s in _.game.allstacks:
  9682.             for c in s.cards:
  9683.                 if not s.basicShallHighlightMatch(c):
  9684.                     continue
  9685.                 
  9686.                 if _.game.shallHighlightMatch(_, card, s, c):
  9687.                     found = 1
  9688.                     if s is _:
  9689.                         j = _.cards.index(c)
  9690.                         if i - 1 == j:
  9691.                             c1 = c
  9692.                             continue
  9693.                         
  9694.                         if i + 1 == j:
  9695.                             c2 = c
  9696.                             continue
  9697.                         
  9698.                     
  9699.                     info.append((s, c, c, col[3]))
  9700.                 
  9701.             
  9702.         
  9703.         if found:
  9704.             if info:
  9705.                 _.game.stats.highlight_cards = _.game.stats.highlight_cards + 1
  9706.             
  9707.             info.append((_, c1, c2, col[1]))
  9708.             return _.game._highlightCards(info, _.game.app.opt.highlight_cards_sleep)
  9709.         
  9710.         return 0
  9711.  
  9712.     
  9713.     def clickHandler(_, event):
  9714.         return 0
  9715.  
  9716.     
  9717.     def middleclickHandler(_, event):
  9718.         if not (_.is_open):
  9719.             return 0
  9720.         
  9721.         i = _._findCard(event)
  9722.         positions = len(_.cards) - i - 1
  9723.         if i < 0 and positions <= 0 or not (_.cards[i].face_up):
  9724.             return 0
  9725.         
  9726.         _.cards[i].item.tkraise()
  9727.         _.game.canvas.update_idletasks()
  9728.         _.game.sleep(_.game.app.opt.raise_card_sleep)
  9729.         _.cards[i].item.lower(_.cards[i + 1].item)
  9730.         _.game.canvas.update_idletasks()
  9731.         return 1
  9732.  
  9733.     
  9734.     def rightclickHandler(_, event):
  9735.         return 0
  9736.  
  9737.     
  9738.     def doubleclickHandler(_, event):
  9739.         return _.clickHandler(event)
  9740.  
  9741.     
  9742.     def controlclickHandler(_, event):
  9743.         return 0
  9744.  
  9745.     
  9746.     def shiftclickHandler(_, event):
  9747.         if _.game.app.opt.highlight_samerank:
  9748.             return _.highlightSameRank(event)
  9749.         
  9750.         return 0
  9751.  
  9752.     
  9753.     def shiftrightclickHandler(_, event):
  9754.         return 0
  9755.  
  9756.     
  9757.     def releaseHandler(_, event, drag, sound = 1):
  9758.         if drag.cards:
  9759.             if sound:
  9760.                 _.game.playSample('nomove')
  9761.             
  9762.             _.moveCardsBackHandler(event, drag)
  9763.         
  9764.  
  9765.     
  9766.     def moveCardsBackHandler(_, event, drag):
  9767.         for card in drag.cards:
  9768.             _._position(card)
  9769.         
  9770.  
  9771.     
  9772.     def __defaultClickEventHandler(_, event, handler, start_drag = 0):
  9773.         if _.game.demo:
  9774.             _.game.stopDemo(event)
  9775.         
  9776.         if _.game.busy:
  9777.             return EVENT_HANDLED
  9778.         
  9779.         if _.game.drag.stack:
  9780.             _.game.drag.stack.cancelDrag(event)
  9781.         
  9782.         if start_drag:
  9783.             r = handler(event)
  9784.             if r <= 0:
  9785.                 sound = r == 0
  9786.                 _.startDrag(event, sound = sound)
  9787.             
  9788.         else:
  9789.             handler(event)
  9790.         return EVENT_HANDLED
  9791.  
  9792.     
  9793.     def __clickEventHandler(_, event):
  9794.         return _._Stack__defaultClickEventHandler(event, _.clickHandler, 1)
  9795.  
  9796.     
  9797.     def __doubleclickEventHandler(_, event):
  9798.         return _._Stack__defaultClickEventHandler(event, _.doubleclickHandler, 1)
  9799.  
  9800.     
  9801.     def __middleclickEventHandler(_, event):
  9802.         return _._Stack__defaultClickEventHandler(event, _.middleclickHandler)
  9803.  
  9804.     
  9805.     def __rightclickEventHandler(_, event):
  9806.         return _._Stack__defaultClickEventHandler(event, _.rightclickHandler)
  9807.  
  9808.     
  9809.     def __controlclickEventHandler(_, event):
  9810.         return _._Stack__defaultClickEventHandler(event, _.controlclickHandler)
  9811.  
  9812.     
  9813.     def __shiftclickEventHandler(_, event):
  9814.         return _._Stack__defaultClickEventHandler(event, _.shiftclickHandler)
  9815.  
  9816.     
  9817.     def __shiftrightclickEventHandler(_, event):
  9818.         return _._Stack__defaultClickEventHandler(event, _.shiftrightclickHandler)
  9819.  
  9820.     
  9821.     def __motionEventHandler(_, event):
  9822.         if _.game.demo:
  9823.             _.game.stopDemo(event)
  9824.         
  9825.         if _.game.busy:
  9826.             return EVENT_HANDLED
  9827.         
  9828.         if 1:
  9829.             drag = _.game.drag
  9830.             if drag.timer is None:
  9831.                 drag.timer = after_idle(_.canvas, _.keepDragTimer)
  9832.             
  9833.             drag.event = event
  9834.         else:
  9835.             _.keepDrag(event)
  9836.         return EVENT_HANDLED
  9837.  
  9838.     
  9839.     def __releaseEventHandler(_, event):
  9840.         if _.game.demo:
  9841.             _.game.stopDemo(event)
  9842.         
  9843.         if _.game.busy:
  9844.             return EVENT_HANDLED
  9845.         
  9846.         _.keepDrag(event)
  9847.         _.finishDrag(event)
  9848.         return EVENT_HANDLED
  9849.  
  9850.     
  9851.     def __enterEventHandler(_, event):
  9852.         _._Stack__leaveEventHandler(None)
  9853.         opt = _.game.app.opt
  9854.         if opt.magnetic_mouse:
  9855.             t = int(opt.magnetic_mouse_time * 1000.0)
  9856.             _.magnetic_mouse_timer = after(_.canvas, t, _._Stack__magneticMouseTimerHandler)
  9857.         
  9858.         return EVENT_HANDLED
  9859.  
  9860.     
  9861.     def __leaveEventHandler(_, event):
  9862.         after_cancel(_.magnetic_mouse.timer)
  9863.         _.magnetic_mouse.timer = None
  9864.         _.magnetic_mouse.event = None
  9865.         return EVENT_HANDLED
  9866.  
  9867.     
  9868.     def __magneticMouseTimerHandler(_):
  9869.         event = _.magnetic_mouse.event
  9870.         _._Stack__leaveEventHandler(None)
  9871.         if event and not (_.game.drag.stack):
  9872.             pass
  9873.         
  9874.  
  9875.     
  9876.     def startDrag(_, event, sound = 1):
  9877.         if not __debug__ and _.game.drag.stack is None:
  9878.             raise AssertionError
  9879.         i = _._findCard(event)
  9880.         if i < 0 or not _.canMoveCards(_.cards[i:]):
  9881.             return None
  9882.         
  9883.         if sound:
  9884.             _.game.playSample('startdrag')
  9885.         
  9886.         _.lastx = event.x
  9887.         _.lasty = event.y
  9888.         game = _.game
  9889.         drag = game.drag
  9890.         drag.start_x = event.x
  9891.         drag.start_y = event.y
  9892.         drag.stack = _
  9893.         drag.noshade_stacks = [
  9894.             _]
  9895.         drag.cards = _.cards[i:]
  9896.         images = game.app.images
  9897.         drag.shadows = _.createShadows(drag.cards)
  9898.         for s in drag.shadows:
  9899.             s.tkraise()
  9900.         
  9901.         (sx, sy) = (-(images.SHADOW_XOFFSET), -(images.SHADOW_YOFFSET))
  9902.         for card in drag.cards:
  9903.             card.tkraise()
  9904.             card.moveBy(sx, sy)
  9905.         
  9906.  
  9907.     
  9908.     def keepDrag(_, event):
  9909.         drag = _.game.drag
  9910.         if not (drag.cards):
  9911.             return None
  9912.         
  9913.         if not __debug__ and _ is drag.stack:
  9914.             raise AssertionError
  9915.         dx = event.x - _.lastx
  9916.         dy = event.y - _.lasty
  9917.         drag.event = None
  9918.  
  9919.     
  9920.     def keepDragTimer(_):
  9921.         drag = _.game.drag
  9922.         after_cancel(drag.timer)
  9923.         drag.timer = None
  9924.         if drag.event:
  9925.             _.keepDrag(drag.event)
  9926.         
  9927.  
  9928.     
  9929.     def createShadows(_, cards, dx = 0, dy = 0):
  9930.         if not (_.game.app.opt.shadow) or _.canvas.preview > 1:
  9931.             return ()
  9932.         
  9933.         l = len(cards)
  9934.         if l == 0 or l > _.max_shadow_cards:
  9935.             return ()
  9936.         
  9937.         images = _.game.app.images
  9938.         (cx, cy) = (cards[0].x, cards[0].y)
  9939.         for c in cards[1:]:
  9940.             cy = c.y
  9941.         
  9942.         (img0, img1) = (images.getShadow(0), images.getShadow(l))
  9943.         return ()
  9944.  
  9945.     
  9946.     def _deleteShade(_):
  9947.         if _.game.drag.shade_img:
  9948.             _.game.drag.shade_img.delete()
  9949.         
  9950.         _.game.drag.shade_img = None
  9951.         _.game.drag.shade_stack = None
  9952.  
  9953.     
  9954.     def _updateShade(_):
  9955.         game = _.game
  9956.         images = game.app.images
  9957.         img = images.getShade()
  9958.         if img is None:
  9959.             return None
  9960.         
  9961.         (CW, CH) = (images.CARDW, images.CARDH)
  9962.         drag = game.drag
  9963.         c = drag.cards[0]
  9964.         stacks = (game.getClosestStack(c, drag.stack),)
  9965.         (r1_0, r1_1, r1_2, r1_3) = (c.x, c.y, c.x + CW, c.y + CH)
  9966.         (sstack, sdiff, sx, sy) = (None, 999999999, 0, 0)
  9967.         for s in stacks:
  9968.             if s.cards:
  9969.                 c = s.cards[-1]
  9970.                 r2 = (c.x, c.y, c.x + CW, c.y + CH)
  9971.             else:
  9972.                 r2 = (s.x, s.y, s.x + CW, s.y + CH)
  9973.             if r1_2 <= r2[0] and r1_3 <= r2[1] and r2[2] <= r1_0 or r2[3] <= r1_1:
  9974.                 continue
  9975.             
  9976.             if s in drag.canshade_stacks:
  9977.                 pass
  9978.             elif s.acceptsCards(drag.stack, drag.cards):
  9979.                 drag.canshade_stacks.append(s)
  9980.             else:
  9981.                 drag.noshade_stacks.append(s)
  9982.             diff = (r1_0 - r2[0]) ** 2 + (r1_1 - r2[1]) ** 2
  9983.             if diff < sdiff:
  9984.                 (sstack, sdiff, sx, sy) = (s, diff, r2[0], r2[1])
  9985.             
  9986.         
  9987.         if sstack is drag.shade_stack:
  9988.             return None
  9989.         
  9990.         if sstack is None:
  9991.             _._deleteShade()
  9992.             return None
  9993.         
  9994.         drag.shade_stack = sstack
  9995.         if drag.shade_img:
  9996.             drag.shade_img.moveTo(sx, sy)
  9997.         else:
  9998.             img = MfxCanvasImage(game.canvas, sx, sy, image = img, anchor = ANCHOR_NW)
  9999.             drag.shade_img = img
  10000.             if drag.shadows:
  10001.                 img.lower(drag.shadows[0])
  10002.             else:
  10003.                 img.lower(drag.cards[0].item)
  10004.  
  10005.     
  10006.     def _stopDrag(_):
  10007.         drag = _.game.drag
  10008.         after_cancel(drag.timer)
  10009.         drag.timer = None
  10010.         _._deleteShade()
  10011.         drag.canshade_stacks = []
  10012.         drag.noshade_stacks = []
  10013.         for s in drag.shadows:
  10014.             s.delete()
  10015.         
  10016.         drag.shadows = []
  10017.         drag.stack = None
  10018.         drag.cards = []
  10019.  
  10020.     
  10021.     def finishDrag(_, event = None):
  10022.         if _.game.app.opt.dragcursor:
  10023.             _.game.canvas.config(cursor = _.game.app.top_cursor)
  10024.         
  10025.         drag = _.game.drag.copy()
  10026.         _._stopDrag()
  10027.         if drag.cards:
  10028.             if not __debug__ and drag.stack is _:
  10029.                 raise AssertionError
  10030.             _.releaseHandler(event, drag)
  10031.         
  10032.  
  10033.     
  10034.     def cancelDrag(_, event = None):
  10035.         if _.game.app.opt.dragcursor:
  10036.             _.game.canvas.config(cursor = _.game.app.top_cursor)
  10037.         
  10038.         drag = _.game.drag.copy()
  10039.         _._stopDrag()
  10040.         if drag.cards:
  10041.             if not __debug__ and drag.stack is _:
  10042.                 raise AssertionError
  10043.             _.moveCardsBackHandler(event, drag)
  10044.         
  10045.  
  10046.  
  10047.  
  10048. class DealRow_StackMethods:
  10049.     
  10050.     def dealRow(_, rows = None, flip = 1, reverse = 0, frames = -1, sound = 0):
  10051.         if rows is None:
  10052.             rows = _.game.s.rows
  10053.         
  10054.         if sound and frames and _.game.app.opt.animations:
  10055.             _.game.startDealSample()
  10056.         
  10057.         n = _.dealToStacks(rows, flip, reverse, frames)
  10058.         if sound:
  10059.             _.game.stopSamples()
  10060.         
  10061.         return n
  10062.  
  10063.     
  10064.     def dealRowAvail(_, rows = None, flip = 1, reverse = 0, frames = -1, sound = 0):
  10065.         if rows is None:
  10066.             rows = _.game.s.rows
  10067.         
  10068.         if sound and frames and _.game.app.opt.animations:
  10069.             _.game.startDealSample()
  10070.         
  10071.         if len(_.cards) < len(rows):
  10072.             rows = rows[:len(_.cards)]
  10073.         
  10074.         n = _.dealToStacks(rows, flip, reverse, frames)
  10075.         if sound:
  10076.             _.game.stopSamples()
  10077.         
  10078.         return n
  10079.  
  10080.     
  10081.     def dealToStacks(_, stacks, flip = 1, reverse = 0, frames = -1):
  10082.         if not (_.cards) or not stacks:
  10083.             return 0
  10084.         
  10085.         if not __debug__ and len(_.cards) >= len(stacks):
  10086.             raise AssertionError
  10087.         old_state = _.game.enterState(_.game.S_DEAL)
  10088.         if reverse:
  10089.             stacks = list(stacks)[:]
  10090.             stacks.reverse()
  10091.         
  10092.         for r in stacks:
  10093.             if not __debug__ and not (_.getCard().face_up):
  10094.                 raise AssertionError
  10095.             0
  10096.             if not __debug__ and r is not _:
  10097.                 raise AssertionError
  10098.             stacks
  10099.             if frames == 0 and _.game.moves.state == _.game.S_INIT:
  10100.                 c = _.removeCard(update = 0)
  10101.                 r.addCard(c, update = 0)
  10102.                 if flip:
  10103.                     c.showFace()
  10104.                 
  10105.             elif flip:
  10106.                 _.game.flipMove(_)
  10107.             
  10108.             _.game.moveMove(1, _, r, frames = frames)
  10109.         
  10110.         _.game.leaveState(old_state)
  10111.         return len(stacks)
  10112.  
  10113.     
  10114.     def dealToStacksOrFoundations(_, stacks, flip = 1, reverse = 0, frames = -1, rank = -1):
  10115.         if rank < 0:
  10116.             rank = _.game.s.foundations[0].cap.base_rank
  10117.         
  10118.         if not (_.cards) or not stacks:
  10119.             return 0
  10120.         
  10121.         old_state = _.game.enterState(_.game.S_DEAL)
  10122.         if reverse:
  10123.             stacks = list(stacks)[:]
  10124.             stacks.reverse()
  10125.         
  10126.         n = 0
  10127.         for r in stacks:
  10128.             if not __debug__ and r is not _:
  10129.                 raise AssertionError
  10130.             0
  10131.             while _.cards:
  10132.                 n = n + 1
  10133.                 if flip:
  10134.                     _.game.flipMove(_)
  10135.                 
  10136.                 if flip and _.cards[-1].rank == rank:
  10137.                     for s in _.game.s.foundations:
  10138.                         if not __debug__ and s is not _:
  10139.                             raise AssertionError
  10140.                         0
  10141.                         if s.acceptsCards(_, _.cards[-1:]):
  10142.                             _.game.moveMove(1, _, s, frames = frames)
  10143.                             break
  10144.                         
  10145.                     
  10146.                 else:
  10147.                     _.game.moveMove(1, _, r, frames = frames)
  10148.                     break
  10149.         
  10150.         _.game.leaveState(old_state)
  10151.         return n
  10152.  
  10153.  
  10154.  
  10155. class DealBaseCard_StackMethods:
  10156.     
  10157.     def dealSingleBaseCard(_, frames = -1, update_saveinfo = 1):
  10158.         c = _.cards[-1]
  10159.         _.dealBaseCards(ncards = 1, frames = frames, update_saveinfo = 0)
  10160.         for s in _.game.s.foundations:
  10161.             s.cap.base_rank = c.rank
  10162.         
  10163.         return c
  10164.  
  10165.     
  10166.     def dealBaseCards(_, ncards = 1, frames = -1, update_saveinfo = 1):
  10167.         if not __debug__ and _.game.moves.state == _.game.S_INIT:
  10168.             raise AssertionError
  10169.         if not __debug__ and not (_.base_cards):
  10170.             raise AssertionError
  10171.         while ncards > 0:
  10172.             if not __debug__ and _.cards:
  10173.                 raise AssertionError
  10174.             c = _.cards[-1]
  10175.             for s in _.game.s.foundations:
  10176.                 pass
  10177.             elif not __debug__ and 0:
  10178.                 raise AssertionError
  10179.             None if not (s.cards) and s.cap.base_suit < 0 or s.cap.base_suit == c.suit else _.game.s.foundations
  10180.             s = None
  10181.             s.cap.base_rank = c.rank
  10182.             if update_saveinfo:
  10183.                 cap = Struct(base_rank = c.rank)
  10184.                 _.game.saveinfo.stack_caps.append((s.id, cap))
  10185.             
  10186.             if not (c.face_up):
  10187.                 _.game.flipMove(_)
  10188.             
  10189.             _.game.moveMove(1, _, s, frames = frames)
  10190.             ncards = ncards - 1
  10191.  
  10192.  
  10193.  
  10194. class TalonStack(Stack, DealRow_StackMethods, DealBaseCard_StackMethods):
  10195.     
  10196.     def __init__(_, x, y, game, max_rounds = 1, num_deal = 1, **cap):
  10197.         Stack.__init__(_, x, y, game, cap = cap)
  10198.         _.max_rounds = max_rounds
  10199.         _.num_deal = num_deal
  10200.         _.resetGame()
  10201.  
  10202.     
  10203.     def resetGame(_):
  10204.         _.round = 1
  10205.         _.base_cards = []
  10206.  
  10207.     
  10208.     def assertStack(_):
  10209.         Stack.assertStack(_)
  10210.         n = _.game.gameinfo.redeals
  10211.         if n < 0:
  10212.             if not __debug__ and _.max_rounds == n:
  10213.                 raise AssertionError
  10214.         elif not __debug__ and _.max_rounds == n + 1:
  10215.             raise AssertionError
  10216.  
  10217.     
  10218.     def clickHandler(_, event):
  10219.         return _.game.dealCards(sound = 1)
  10220.  
  10221.     
  10222.     def rightclickHandler(_, event):
  10223.         return _.clickHandler(event)
  10224.  
  10225.     
  10226.     def canDealCards(_):
  10227.         return len(_.cards) > 0
  10228.  
  10229.     
  10230.     def dealCards(_, sound = 0):
  10231.         pass
  10232.  
  10233.     
  10234.     def removeAllCards(_):
  10235.         for stack in _.game.allstacks:
  10236.             while stack.cards:
  10237.                 stack.removeCard(unhide = 0, update = 0)
  10238.                 continue
  10239.                 0
  10240.         
  10241.         for stack in _.game.allstacks:
  10242.             stack.updateText()
  10243.         
  10244.  
  10245.     
  10246.     def updateText(_, update_rounds = 1, update_redeal = 1):
  10247.         Stack.updateText(_)
  10248.         if update_rounds and _.game.preview <= 1:
  10249.             if _.texts.rounds is not None:
  10250.                 t = 'Round %d' % _.round
  10251.                 _.texts.rounds.config(text = t)
  10252.             
  10253.         
  10254.         if update_redeal:
  10255.             deal = _.canDealCards() != 0
  10256.             if _.images.redeal is not None:
  10257.                 img = _.getRedealImages()[deal]
  10258.                 if img is not None and img is not _.images.redeal_img:
  10259.                     _.images.redeal.config(image = img)
  10260.                     _.images.redeal_img = img
  10261.                 
  10262.                 t = ('', 'Redeal')[deal]
  10263.             else:
  10264.                 t = ('Stop', 'Redeal')[deal]
  10265.             if _.texts.redeal is not None and _.game.preview <= 1:
  10266.                 if t != _.texts.redeal_str:
  10267.                     _.texts.redeal.config(text = t)
  10268.                     _.texts.redeal_str = t
  10269.                 
  10270.             
  10271.         
  10272.  
  10273.     
  10274.     def prepareView(_):
  10275.         Stack.prepareView(_)
  10276.         if not (_.is_visible) or _.images.bottom is None:
  10277.             return None
  10278.         
  10279.         if _.images.redeal is not None or _.texts.redeal is not None:
  10280.             return None
  10281.         
  10282.         if _.game.preview > 1:
  10283.             return None
  10284.         
  10285.         images = _.game.app.images
  10286.         (cx, cy, ca) = (_.x + images.CARDW / 2, _.y + images.CARDH / 2, 'center')
  10287.         if images.CARDW >= 54 and images.CARDH >= 54:
  10288.             img = _.getRedealImages()[_.max_rounds != 1]
  10289.             if img is not None:
  10290.                 _.images.redeal = MfxCanvasImage(_.game.canvas, cx, cy, image = img, anchor = 'center')
  10291.                 _.images.redeal_img = img
  10292.                 _.images.redeal.tkraise(_.top_bottom)
  10293.                 _.images.redeal.addtag(_.group)
  10294.                 _.top_bottom = _.images.redeal
  10295.                 if images.CARDH >= 90:
  10296.                     (cy, ca) = (_.y + images.CARDH - 4, 's')
  10297.                 else:
  10298.                     ca = None
  10299.             
  10300.         
  10301.         if images.CARDW >= 43 and ca:
  10302.             if _.max_rounds != 1:
  10303.                 images = _.game.app.images
  10304.                 _.texts.redeal = MfxCanvasText(_.game.canvas, cx, cy, anchor = ca)
  10305.                 _.texts.redeal_str = ''
  10306.                 _.texts.redeal.tkraise(_.top_bottom)
  10307.                 _.texts.redeal.addtag(_.group)
  10308.                 _.top_bottom = _.texts.redeal
  10309.             
  10310.         
  10311.  
  10312.     
  10313.     def getBottomImage(_):
  10314.         return _.game.app.images.getTalonBottom()
  10315.  
  10316.     
  10317.     def getRedealImages(_):
  10318.         return _.game.app.gimages.redeal
  10319.  
  10320.  
  10321.  
  10322. class DealRowTalonStack(TalonStack):
  10323.     
  10324.     def dealCards(_, sound = 0):
  10325.         return _.dealRowAvail(sound = sound)
  10326.  
  10327.  
  10328.  
  10329. class InitialDealTalonStack(TalonStack):
  10330.     
  10331.     def initBindings(_):
  10332.         pass
  10333.  
  10334.     
  10335.     def getBottomImage(_):
  10336.         return None
  10337.  
  10338.  
  10339.  
  10340. class OpenStack(Stack):
  10341.     
  10342.     def __init__(_, x, y, game, **cap):
  10343.         kwdefault(cap, max_move = 1, max_accept = 0, max_cards = 999999)
  10344.         Stack.__init__(_, x, y, game, cap = cap)
  10345.  
  10346.     
  10347.     def acceptsCards(_, from_stack, cards):
  10348.         return _.basicAcceptsCards(from_stack, cards)
  10349.  
  10350.     
  10351.     def canMoveCards(_, cards):
  10352.         return _.basicCanMoveCards(cards)
  10353.  
  10354.     
  10355.     def canFlipCard(_):
  10356.         if _.basicIsBlocked() or not (_.cards):
  10357.             return 0
  10358.         
  10359.         return not (_.cards[-1].face_up)
  10360.  
  10361.     
  10362.     def canDropCards(_, stacks):
  10363.         if _.basicIsBlocked() or not (_.cards):
  10364.             return (None, 0)
  10365.         
  10366.         cards = _.cards[-1:]
  10367.         if _.canMoveCards(cards):
  10368.             for s in stacks:
  10369.                 pass
  10370.             
  10371.         
  10372.         return (None, 0)
  10373.  
  10374.     
  10375.     def clickHandler(_, event):
  10376.         (flipstacks, dropstacks, quickstacks) = _.game.getAutoStacks(event)
  10377.         if _ in flipstacks and _.canFlipCard():
  10378.             _.playFlipMove()
  10379.             return -1
  10380.         
  10381.         return 0
  10382.  
  10383.     
  10384.     def rightclickHandler(_, event):
  10385.         if _.doubleclickHandler(event):
  10386.             return 1
  10387.         
  10388.         if _.game.app.opt.quickplay:
  10389.             (flipstacks, dropstacks, quickstacks) = _.game.getAutoStacks(event)
  10390.             if _ in quickstacks:
  10391.                 n = _.quickPlayHandler(event)
  10392.                 _.game.stats.quickplay_moves = _.game.stats.quickplay_moves + n
  10393.                 return n
  10394.             
  10395.         
  10396.         return 0
  10397.  
  10398.     
  10399.     def doubleclickHandler(_, event):
  10400.         (flipstacks, dropstacks, quickstacks) = _.game.getAutoStacks(event)
  10401.         if _ in flipstacks and _.canFlipCard():
  10402.             _.playFlipMove()
  10403.             return -1
  10404.         
  10405.         if _ in dropstacks:
  10406.             (to_stack, ncards) = _.canDropCards(_.game.s.foundations)
  10407.             if to_stack:
  10408.                 _.game.playSample('autodrop', priority = 30)
  10409.                 _.playMoveMove(ncards, to_stack, sound = 0)
  10410.                 return 1
  10411.             
  10412.         
  10413.         return 0
  10414.  
  10415.     
  10416.     def controlclickHandler(_, event):
  10417.         if _.game.app.opt.highlight_cards:
  10418.             return _.highlightMatchingCards(event)
  10419.         
  10420.         return 0
  10421.  
  10422.     
  10423.     def releaseHandler(_, event, drag, sound = 1):
  10424.         cards = drag.cards
  10425.         if event is not None:
  10426.             (dx, dy) = (event.x - drag.start_x, event.y - drag.start_y)
  10427.             if abs(dx) < 10 and abs(dy) < 10:
  10428.                 Stack.releaseHandler(_, event, drag, sound = sound)
  10429.                 return None
  10430.             
  10431.         
  10432.         stack = _.game.getClosestStack(cards[0], _)
  10433.         if not stack and stack is _ or not stack.acceptsCards(_, cards):
  10434.             Stack.releaseHandler(_, event, drag, sound = sound)
  10435.         else:
  10436.             _.playMoveMove(len(cards), stack, frames = 0, sound = sound)
  10437.  
  10438.     
  10439.     def quickPlayHandler(_, event, from_stacks = None, to_stacks = None):
  10440.         if from_stacks is None:
  10441.             from_stacks = _.game.sg.dropstacks
  10442.         
  10443.         if to_stacks is None:
  10444.             to_stacks = _.game.s.foundations + _.game.sg.dropstacks
  10445.         
  10446.         moves = []
  10447.         if moves:
  10448.             moves.sort()
  10449.             moves.reverse()
  10450.             if moves[0][0] >= 0:
  10451.                 moves[0][3].playMoveMove(moves[0][2], moves[0][4])
  10452.                 return 1
  10453.             
  10454.         
  10455.         return 0
  10456.  
  10457.  
  10458.  
  10459. class AbstractFoundationStack(OpenStack):
  10460.     
  10461.     def __init__(_, x, y, game, suit, **cap):
  10462.         kwdefault(cap, suit = suit, base_suit = suit, base_rank = ACE, dir = 1, max_accept = 1, max_cards = 13)
  10463.         apply(OpenStack.__init__, (_, x, y, game), cap)
  10464.  
  10465.     
  10466.     def canDropCards(_, stacks):
  10467.         return (None, 0)
  10468.  
  10469.     
  10470.     def clickHandler(_, event):
  10471.         return 0
  10472.  
  10473.     
  10474.     def rightclickHandler(_, event):
  10475.         return 0
  10476.  
  10477.     
  10478.     def quickPlayHandler(_, event, from_stacks = None, to_stacks = None):
  10479.         return 0
  10480.  
  10481.     
  10482.     def getBottomImage(_):
  10483.         return _.game.app.images.getSuitBottom(_.cap.base_suit)
  10484.  
  10485.  
  10486.  
  10487. class SS_FoundationStack(AbstractFoundationStack):
  10488.     
  10489.     def acceptsCards(_, from_stack, cards):
  10490.         if not AbstractFoundationStack.acceptsCards(_, from_stack, cards):
  10491.             return 0
  10492.         
  10493.         if _.cards:
  10494.             if (_.cards[-1].rank + _.cap.dir) % _.cap.mod != cards[0].rank:
  10495.                 return 0
  10496.             
  10497.         
  10498.         return 1
  10499.  
  10500.  
  10501.  
  10502. class RK_FoundationStack(SS_FoundationStack):
  10503.     
  10504.     def __init__(_, x, y, game, suit = ANY_SUIT, **cap):
  10505.         apply(SS_FoundationStack.__init__, (_, x, y, game, suit), cap)
  10506.  
  10507.     
  10508.     def assertStack(_):
  10509.         SS_FoundationStack.assertStack(_)
  10510.         if not __debug__ and _.cap.suit == ANY_SUIT:
  10511.             raise AssertionError
  10512.         if not __debug__ and _.cap.color == ANY_COLOR:
  10513.             raise AssertionError
  10514.  
  10515.  
  10516.  
  10517. class AC_FoundationStack(SS_FoundationStack):
  10518.     
  10519.     def __init__(_, x, y, game, suit, **cap):
  10520.         kwdefault(cap, base_suit = suit)
  10521.         apply(SS_FoundationStack.__init__, (_, x, y, game, ANY_SUIT), cap)
  10522.  
  10523.     
  10524.     def acceptsCards(_, from_stack, cards):
  10525.         if not SS_FoundationStack.acceptsCards(_, from_stack, cards):
  10526.             return 0
  10527.         
  10528.         if _.cards:
  10529.             if cards[0].color == _.cards[-1].color:
  10530.                 return 0
  10531.             
  10532.         
  10533.         return 1
  10534.  
  10535.  
  10536.  
  10537. class SequenceStack_StackMethods:
  10538.     
  10539.     def _isSequence(_, cards):
  10540.         raise SubclassResponsibility
  10541.  
  10542.     
  10543.     def _isAcceptableSequence(_, cards):
  10544.         return _._isSequence(cards)
  10545.  
  10546.     
  10547.     def _isMoveableSequence(_, cards):
  10548.         return _._isSequence(cards)
  10549.  
  10550.     
  10551.     def acceptsCards(_, from_stack, cards):
  10552.         if not _.basicAcceptsCards(from_stack, cards):
  10553.             return 0
  10554.         
  10555.         if not _._isAcceptableSequence(cards):
  10556.             return 0
  10557.         
  10558.         if _.cards and not _._isAcceptableSequence([
  10559.             _.cards[-1]] + cards):
  10560.             return 0
  10561.         
  10562.         return 1
  10563.  
  10564.     
  10565.     def canMoveCards(_, cards):
  10566.         if _.basicCanMoveCards(cards):
  10567.             pass
  10568.         return _._isMoveableSequence(cards)
  10569.  
  10570.  
  10571.  
  10572. class BasicRowStack(OpenStack):
  10573.     
  10574.     def __init__(_, x, y, game, **cap):
  10575.         kwdefault(cap, dir = -1, base_rank = ANY_RANK)
  10576.         apply(OpenStack.__init__, (_, x, y, game), cap)
  10577.         _.CARD_YOFFSET = game.app.images.CARD_YOFFSET
  10578.  
  10579.  
  10580.  
  10581. class SequenceRowStack(SequenceStack_StackMethods, BasicRowStack):
  10582.     
  10583.     def __init__(_, x, y, game, **cap):
  10584.         kwdefault(cap, max_move = 999999, max_accept = 999999)
  10585.         apply(BasicRowStack.__init__, (_, x, y, game), cap)
  10586.  
  10587.  
  10588.  
  10589. class AC_RowStack(SequenceRowStack):
  10590.     
  10591.     def _isSequence(_, cards):
  10592.         return isAlternateColorSequence(cards, _.cap.mod, _.cap.dir)
  10593.  
  10594.  
  10595.  
  10596. class SC_RowStack(SequenceRowStack):
  10597.     
  10598.     def _isSequence(_, cards):
  10599.         return isSameColorSequence(cards, _.cap.mod, _.cap.dir)
  10600.  
  10601.  
  10602.  
  10603. class SS_RowStack(SequenceRowStack):
  10604.     
  10605.     def _isSequence(_, cards):
  10606.         return isSameSuitSequence(cards, _.cap.mod, _.cap.dir)
  10607.  
  10608.  
  10609.  
  10610. class RK_RowStack(SequenceRowStack):
  10611.     
  10612.     def _isSequence(_, cards):
  10613.         return isRankSequence(cards, _.cap.mod, _.cap.dir)
  10614.  
  10615.  
  10616.  
  10617. class FreeCell_AC_RowStack(AC_RowStack):
  10618.     
  10619.     def canMoveCards(_, cards):
  10620.         max_move = getNumberOfFreeStacks(_.game.s.reserves) + 1
  10621.         if len(cards) <= max_move:
  10622.             pass
  10623.         return AC_RowStack.canMoveCards(_, cards)
  10624.  
  10625.  
  10626.  
  10627. class FreeCell_SS_RowStack(SS_RowStack):
  10628.     
  10629.     def canMoveCards(_, cards):
  10630.         max_move = getNumberOfFreeStacks(_.game.s.reserves) + 1
  10631.         if len(cards) <= max_move:
  10632.             pass
  10633.         return SS_RowStack.canMoveCards(_, cards)
  10634.  
  10635.  
  10636.  
  10637. class Spider_AC_RowStack(AC_RowStack):
  10638.     
  10639.     def _isAcceptableSequence(_, cards):
  10640.         return isRankSequence(cards, _.cap.mod, _.cap.dir)
  10641.  
  10642.  
  10643.  
  10644. class Spider_SS_RowStack(SS_RowStack):
  10645.     
  10646.     def _isAcceptableSequence(_, cards):
  10647.         return isRankSequence(cards, _.cap.mod, _.cap.dir)
  10648.  
  10649.  
  10650.  
  10651. class Yukon_AC_RowStack(BasicRowStack):
  10652.     
  10653.     def __init__(_, x, y, game, **cap):
  10654.         kwdefault(cap, max_move = 999999, max_accept = 999999)
  10655.         apply(BasicRowStack.__init__, (_, x, y, game), cap)
  10656.  
  10657.     
  10658.     def _isSequence(_, c1, c2):
  10659.         if (c1.rank + _.cap.dir) % _.cap.mod == c2.rank:
  10660.             pass
  10661.         return c1.color != c2.color
  10662.  
  10663.     
  10664.     def acceptsCards(_, from_stack, cards):
  10665.         if not _.basicAcceptsCards(from_stack, cards):
  10666.             return 0
  10667.         
  10668.         if _.cards and not _._isSequence(_.cards[-1], cards[0]):
  10669.             return 0
  10670.         
  10671.         return 1
  10672.  
  10673.  
  10674.  
  10675. class Yukon_SS_RowStack(Yukon_AC_RowStack):
  10676.     
  10677.     def _isSequence(_, c1, c2):
  10678.         if (c1.rank + _.cap.dir) % _.cap.mod == c2.rank:
  10679.             pass
  10680.         return c1.suit == c2.suit
  10681.  
  10682.  
  10683.  
  10684. class KingAC_RowStack(AC_RowStack):
  10685.     
  10686.     def __init__(_, x, y, game, **cap):
  10687.         kwdefault(cap, base_rank = KING)
  10688.         apply(AC_RowStack.__init__, (_, x, y, game), cap)
  10689.  
  10690.  
  10691.  
  10692. class KingSS_RowStack(SS_RowStack):
  10693.     
  10694.     def __init__(_, x, y, game, **cap):
  10695.         kwdefault(cap, base_rank = KING)
  10696.         apply(SS_RowStack.__init__, (_, x, y, game), cap)
  10697.  
  10698.  
  10699.  
  10700. class KingRK_RowStack(RK_RowStack):
  10701.     
  10702.     def __init__(_, x, y, game, **cap):
  10703.         kwdefault(cap, base_rank = KING)
  10704.         apply(RK_RowStack.__init__, (_, x, y, game), cap)
  10705.  
  10706.  
  10707.  
  10708. class WasteStack(OpenStack):
  10709.     pass
  10710.  
  10711.  
  10712. class WasteTalonStack(TalonStack):
  10713.     
  10714.     def __init__(_, x, y, game, max_rounds, num_deal = 1, waste = None, **cap):
  10715.         apply(TalonStack.__init__, (_, x, y, game, max_rounds, num_deal), cap)
  10716.         _.waste = waste
  10717.  
  10718.     
  10719.     def prepareStack(_):
  10720.         TalonStack.prepareStack(_)
  10721.         if _.waste is None:
  10722.             _.waste = _.game.s.waste
  10723.         
  10724.  
  10725.     
  10726.     def canDealCards(_):
  10727.         waste = _.waste
  10728.         if _.cards:
  10729.             num_cards = min(len(_.cards), _.num_deal)
  10730.             return len(waste.cards) + num_cards <= waste.cap.max_cards
  10731.         elif waste.cards and _.round != _.max_rounds:
  10732.             return 1
  10733.         
  10734.         return 0
  10735.  
  10736.     
  10737.     def dealCards(_, sound = 0):
  10738.         old_state = _.game.enterState(_.game.S_DEAL)
  10739.         num_cards = 0
  10740.         waste = _.waste
  10741.         if _.cards:
  10742.             if sound and not (_.game.demo):
  10743.                 _.game.playSample('dealwaste')
  10744.             
  10745.             num_cards = min(len(_.cards), _.num_deal)
  10746.             if not __debug__ and len(waste.cards) + num_cards <= waste.cap.max_cards:
  10747.                 raise AssertionError
  10748.             for i in range(num_cards):
  10749.                 _.game.moveMove(1, _, waste, frames = 4, shadow = 0)
  10750.                 _.fillStack()
  10751.             
  10752.         elif waste.cards and _.round != _.max_rounds:
  10753.             if sound:
  10754.                 _.game.playSample('turnwaste', priority = 20)
  10755.             
  10756.             num_cards = len(waste.cards)
  10757.             _.game.turnStackMove(waste, _, update_flags = 1)
  10758.         
  10759.         _.game.leaveState(old_state)
  10760.         return num_cards
  10761.  
  10762.  
  10763.  
  10764. class FaceUpWasteTalonStack(WasteTalonStack):
  10765.     
  10766.     def canFlipCard(_):
  10767.         if len(_.cards) > 0:
  10768.             pass
  10769.         return not (_.cards[-1].face_up)
  10770.  
  10771.     
  10772.     def fillStack(_):
  10773.         if _.canFlipCard():
  10774.             _.game.flipMove(_)
  10775.         
  10776.  
  10777.  
  10778.  
  10779. class OpenTalonStack(TalonStack, OpenStack):
  10780.     canMoveCards = OpenStack.canMoveCards
  10781.     canDropCards = OpenStack.canDropCards
  10782.     releaseHandler = OpenStack.releaseHandler
  10783.     
  10784.     def __init__(_, x, y, game, **cap):
  10785.         kwdefault(cap, max_move = 1)
  10786.         apply(TalonStack.__init__, (_, x, y, game), cap)
  10787.  
  10788.     
  10789.     def canDealCards(_):
  10790.         return 0
  10791.  
  10792.     
  10793.     def canFlipCard(_):
  10794.         if len(_.cards) > 0:
  10795.             pass
  10796.         return not (_.cards[-1].face_up)
  10797.  
  10798.     
  10799.     def fillStack(_):
  10800.         if _.canFlipCard():
  10801.             _.game.flipMove(_)
  10802.         
  10803.  
  10804.     
  10805.     def clickHandler(_, event):
  10806.         if _.canDealCards():
  10807.             return TalonStack.clickHandler(_, event)
  10808.         else:
  10809.             return OpenStack.clickHandler(_, event)
  10810.  
  10811.  
  10812.  
  10813. class ReserveStack(OpenStack):
  10814.     
  10815.     def __init__(_, x, y, game, **cap):
  10816.         kwdefault(cap, max_accept = 1, max_cards = 1)
  10817.         apply(OpenStack.__init__, (_, x, y, game), cap)
  10818.  
  10819.     
  10820.     def getBottomImage(_):
  10821.         return _.game.app.images.getReserveBottom()
  10822.  
  10823.  
  10824.  
  10825. class InvisibleStack(Stack):
  10826.     
  10827.     def __init__(_, game, **cap):
  10828.         (x, y) = (-500, -500 - len(game.allstacks))
  10829.         kwdefault(cap, max_move = 0, max_accept = 0)
  10830.         Stack.__init__(_, x, y, game, cap = cap)
  10831.  
  10832.     
  10833.     def assertStack(_):
  10834.         Stack.assertStack(_)
  10835.         if not __debug__ and not (_.is_visible):
  10836.             raise AssertionError
  10837.  
  10838.     
  10839.     def initBindings(_):
  10840.         pass
  10841.  
  10842.     
  10843.     def getBottomImage(_):
  10844.         return None
  10845.  
  10846.  
  10847.  
  10848. class StackWrapper:
  10849.     
  10850.     def __init__(_, stack_class, **cap):
  10851.         if not __debug__ and type(stack_class) is types.ClassType:
  10852.             raise AssertionError
  10853.         if not __debug__ and issubclass(stack_class, Stack):
  10854.             raise AssertionError
  10855.         _.stack_class = stack_class
  10856.         _.cap = cap
  10857.  
  10858.     
  10859.     def __call__(_, x, y, game, **cap):
  10860.         c = _.cap.copy()
  10861.         apply(kwdefault, (c,), cap)
  10862.         return apply(_.stack_class, (x, y, game), c)
  10863.  
  10864.  
  10865.  
  10866. class WeakStackWrapper(StackWrapper):
  10867.     
  10868.     def __call__(_, x, y, game, **cap):
  10869.         apply(kwdefault, (cap,), _.cap)
  10870.         return apply(_.stack_class, (x, y, game), cap)
  10871.  
  10872.  
  10873.  
  10874. class FullStackWrapper(StackWrapper):
  10875.     
  10876.     def __call__(_, x, y, game, **cap):
  10877.         return apply(_.stack_class, (x, y, game), _.cap)
  10878.  
  10879.  
  10880.  
  10881. class HintInterface:
  10882.     
  10883.     def __init__(_, game, level):
  10884.         pass
  10885.  
  10886.     
  10887.     def getHints(_, taken_hint = None):
  10888.         return []
  10889.  
  10890.  
  10891.  
  10892. class AbstractHint(HintInterface):
  10893.     
  10894.     def __init__(_, game, level):
  10895.         _.game = game
  10896.         _.level = level
  10897.         _.score_flatten_value = 0
  10898.         if _.level == 0:
  10899.             _.score_flatten_value = 10000
  10900.         
  10901.         _.bonus_color = None
  10902.         _._AbstractHint__clones = []
  10903.         _.reset()
  10904.  
  10905.     
  10906.     def __del__(_):
  10907.         _.reset()
  10908.  
  10909.     
  10910.     def reset(_):
  10911.         _.hints = []
  10912.         _.max_score = 0
  10913.         _._AbstractHint__destructClones()
  10914.  
  10915.     
  10916.     class AClonedStack:
  10917.         
  10918.         def __init__(_, stack, stackcards):
  10919.             _.__class__ = stack.__class__
  10920.             stack.copyModel(_)
  10921.             _.cards = stackcards[:]
  10922.  
  10923.  
  10924.     
  10925.     def ClonedStack(_, stack, stackcards):
  10926.         s = _.AClonedStack(stack, stackcards)
  10927.         _._AbstractHint__clones.append(s)
  10928.         return s
  10929.  
  10930.     
  10931.     def __destructClones(_):
  10932.         for s in _._AbstractHint__clones:
  10933.             s.__class__ = _.AClonedStack
  10934.             destruct(s)
  10935.         
  10936.         _._AbstractHint__clones = []
  10937.  
  10938.     
  10939.     def addHint(_, score, ncards, from_stack, to_stack, text_color = None, forced_move = None):
  10940.         if score < 0:
  10941.             return None
  10942.         
  10943.         _.max_score = max(_.max_score, score)
  10944.         if _.score_flatten_value > 0:
  10945.             score = (score / _.score_flatten_value) * _.score_flatten_value
  10946.         
  10947.         if text_color is None:
  10948.             text_color = _.BLACK
  10949.         
  10950.         if __debug__:
  10951.             if not forced_move is None or len(forced_move) == 7:
  10952.                 raise AssertionError
  10953.         pos = -len(_.hints)
  10954.         ah = (int(score), pos, ncards, from_stack, to_stack, text_color, forced_move)
  10955.         _.hints.append(ah)
  10956.  
  10957.     
  10958.     def __returnHints(_):
  10959.         hints = _.hints
  10960.         _.reset()
  10961.         hints.sort()
  10962.         hints.reverse()
  10963.         return hints
  10964.  
  10965.     SCORE_FLIP = 100000
  10966.     SCORE_DEAL = 0
  10967.     
  10968.     def getHints(_, taken_hint = None):
  10969.         _.reset()
  10970.         game = _.game
  10971.         if taken_hint and taken_hint[6]:
  10972.             return [
  10973.                 taken_hint[6]]
  10974.         
  10975.         if _.level >= 2:
  10976.             for r in game.allstacks:
  10977.                 if r.canFlipCard():
  10978.                     _.addHint(_.SCORE_FLIP, 1, r, r)
  10979.                     if _.SCORE_FLIP >= 90000:
  10980.                         return _._AbstractHint__returnHints()
  10981.                     
  10982.                 
  10983.             
  10984.         
  10985.         _.computeHints()
  10986.         if _.level >= 2:
  10987.             if game.canDealCards():
  10988.                 _.addHint(_.SCORE_DEAL, 0, game.s.talon, None)
  10989.             
  10990.         
  10991.         return _._AbstractHint__returnHints()
  10992.  
  10993.     
  10994.     def computeHints(_):
  10995.         pass
  10996.  
  10997.     
  10998.     def _defaultShallMovePile(_, from_stack, to_stack, pile, rpile):
  10999.         if from_stack is to_stack or not to_stack.acceptsCards(from_stack, pile):
  11000.             return 0
  11001.         
  11002.         return 1
  11003.  
  11004.     
  11005.     def _cautiousShallMovePile(_, from_stack, to_stack, pile, rpile):
  11006.         if from_stack is to_stack or not to_stack.acceptsCards(from_stack, pile):
  11007.             return 0
  11008.         
  11009.         rr = _.ClonedStack(from_stack, stackcards = rpile)
  11010.         if rr.acceptsCards(to_stack, pile):
  11011.             return 0
  11012.         
  11013.         return 1
  11014.  
  11015.     
  11016.     def _cautiousDemoShallMovePile(_, from_stack, to_stack, pile, rpile):
  11017.         if from_stack is to_stack or not to_stack.acceptsCards(from_stack, pile):
  11018.             return 0
  11019.         
  11020.         if _.level >= 2:
  11021.             rr = _.ClonedStack(from_stack, stackcards = rpile)
  11022.             if rr.acceptsCards(to_stack, pile):
  11023.                 return 0
  11024.             
  11025.         
  11026.         return 1
  11027.  
  11028.     shallMovePile = _defaultShallMovePile
  11029.     
  11030.     def _canDropAllCards(_, from_stack, stacks, stackcards):
  11031.         if not __debug__ and not (from_stack in stacks):
  11032.             raise AssertionError
  11033.         return 0
  11034.         cards = pile[:]
  11035.         cards.reverse()
  11036.         for card in cards:
  11037.             for s in stacks:
  11038.                 pass
  11039.             else:
  11040.                 return 0
  11041.         
  11042.         return 1
  11043.  
  11044.     K = KING + 1
  11045.     BLACK = 'black'
  11046.     RED = 'red'
  11047.     BLUE = 'blue'
  11048.  
  11049.  
  11050. class DefaultHint(AbstractHint):
  11051.     
  11052.     def _preferHighRankMoves(_):
  11053.         return 0
  11054.  
  11055.     BONUS_DROP_CARD = 300
  11056.     BONUS_SAME_SUIT_MOVE = 200
  11057.     BONUS_NORMAL_MOVE = 100
  11058.     
  11059.     def _getMoveCardBonus(_, r, t, pile, rpile):
  11060.         if not __debug__ and pile:
  11061.             raise AssertionError
  11062.         bonus = 0
  11063.         if rpile:
  11064.             rr = _.ClonedStack(r, stackcards = rpile)
  11065.             if rr.canDropCards(_.game.s.foundations)[0]:
  11066.                 bonus = _.BONUS_DROP_CARD
  11067.             
  11068.         
  11069.         if t.cards and t.cards[-1].suit == pile[0].suit:
  11070.             bonus = bonus + _.BONUS_SAME_SUIT_MOVE + 1 + pile[0].rank
  11071.         elif _._preferHighRankMoves():
  11072.             bonus = bonus + _.BONUS_NORMAL_MOVE + 1 + pile[0].rank
  11073.         elif rpile:
  11074.             bonus = bonus + _.BONUS_NORMAL_MOVE + (_.K - rpile[-1].rank)
  11075.         else:
  11076.             bonus = bonus + _.BONUS_NORMAL_MOVE + 1 + pile[0].rank
  11077.         return bonus
  11078.  
  11079.     BONUS_FLIP_CARD = 1500
  11080.     
  11081.     def _getFlipSpecialBonus(_, r, t, pile, rpile):
  11082.         if __debug__:
  11083.             if not pile and rpile:
  11084.                 raise AssertionError
  11085.         bonus = max(_.BONUS_FLIP_CARD - len(rpile), 0)
  11086.         return bonus
  11087.  
  11088.     BONUS_CREATE_EMPTY_ROW = 9000
  11089.     BONUS_CAN_DROP_ALL_CARDS = 4000
  11090.     BONUS_CAN_CREATE_EMPTY_ROW = 2000
  11091.     
  11092.     def _getMoveSpecialBonus(_, r, t, pile, rpile):
  11093.         if not rpile:
  11094.             return _.BONUS_CREATE_EMPTY_ROW
  11095.         
  11096.         if not (rpile[-1].face_up):
  11097.             return _._getFlipSpecialBonus(r, t, pile, rpile)
  11098.         
  11099.         if _._canDropAllCards(r, _.game.s.foundations, stackcards = rpile):
  11100.             _.bonus_color = _.RED
  11101.             return _.BONUS_CAN_DROP_ALL_CARDS + _.BONUS_CAN_CREATE_EMPTY_ROW
  11102.         
  11103.         if r.canMoveCards(rpile):
  11104.             for x in _.game.s.rows:
  11105.                 if x.acceptsCards(r, rpile):
  11106.                     _.bonus_color = _.BLUE
  11107.                     return _.BONUS_CAN_CREATE_EMPTY_ROW
  11108.                 
  11109.             
  11110.         
  11111.         return 0
  11112.  
  11113.     
  11114.     def _getMovePileScore(_, score, color, r, t, pile, rpile):
  11115.         if not __debug__ and pile:
  11116.             raise AssertionError
  11117.         _.bonus_color = color
  11118.         b1 = _._getMoveSpecialBonus(r, t, pile, rpile)
  11119.         if __debug__:
  11120.             if b1 <= b1:
  11121.                 pass
  11122.             elif not b1 <= 9000:
  11123.                 raise AssertionError
  11124.         b2 = _._getMoveCardBonus(r, t, pile, rpile)
  11125.         if __debug__:
  11126.             if b2 <= b2:
  11127.                 pass
  11128.             elif not b2 <= 999:
  11129.                 raise AssertionError
  11130.         return (score + b1 + b2, _.bonus_color)
  11131.  
  11132.     
  11133.     def _getMoveWasteScore(_, score, color, r, t, pile, rpile):
  11134.         if not __debug__ and pile:
  11135.             raise AssertionError
  11136.         _.bonus_color = color
  11137.         score = 30000
  11138.         if t.cards:
  11139.             score = 31000
  11140.         
  11141.         b2 = _._getMoveCardBonus(r, t, pile, rpile)
  11142.         if __debug__:
  11143.             if b2 <= b2:
  11144.                 pass
  11145.             elif not b2 <= 999:
  11146.                 raise AssertionError
  11147.         return (score + b2, _.bonus_color)
  11148.  
  11149.     
  11150.     def _getDropCardScore(_, score, color, r, t, ncards):
  11151.         if not __debug__ and t is not r:
  11152.             raise AssertionError
  11153.         if ncards > 1:
  11154.             return (93000, color)
  11155.         
  11156.         pile = r.cards
  11157.         c = pile[-1]
  11158.         if t.cap.base_rank < 0:
  11159.             d = len(t.cards)
  11160.         else:
  11161.             d = (c.rank - t.cap.base_rank) % t.cap.mod
  11162.             if d > t.cap.mod / 2:
  11163.                 d = d - t.cap.mod
  11164.             
  11165.         if abs(d) <= 1:
  11166.             score = 92000
  11167.         elif r in _.game.sg.talonstacks:
  11168.             score = 25000
  11169.         elif len(pile) == 1:
  11170.             score = 91000
  11171.         elif _._canDropAllCards(r, _.game.s.foundations, stackcards = pile[:-1]):
  11172.             score = 90000
  11173.             color = _.RED
  11174.         else:
  11175.             score = 50000
  11176.         score = score + (_.K - c.rank)
  11177.         return (score, color)
  11178.  
  11179.     
  11180.     def computeHints(_):
  11181.         game = _.game
  11182.         _.step010(game.sg.dropstacks, game.s.rows)
  11183.         if not (_.hints) and _.level >= 1:
  11184.             _.step020(game.s.rows, game.s.foundations)
  11185.         
  11186.         if not (_.hints) and _.level >= 1:
  11187.             _.step030(game.s.foundations, game.s.rows, game.sg.dropstacks)
  11188.         
  11189.         if not (_.hints):
  11190.             _.step040(game.s.rows, game.sg.reservestacks)
  11191.         
  11192.         if not (_.hints):
  11193.             _.step050(game.sg.reservestacks, game.s.rows)
  11194.         
  11195.  
  11196.     
  11197.     def step010(_, dropstacks, rows):
  11198.         for r in dropstacks:
  11199.             (t, ncards) = r.canDropCards(_.game.s.foundations)
  11200.             if t:
  11201.                 (score, color) = (0, None)
  11202.                 (score, color) = _._getDropCardScore(score, color, r, t, ncards)
  11203.                 _.addHint(score, ncards, r, t, color)
  11204.                 if score >= 90000:
  11205.                     break
  11206.                 
  11207.             
  11208.             for pile in _.step010b_getPiles(r):
  11209.                 pass
  11210.             
  11211.         
  11212.  
  11213.     
  11214.     def step010b_getPiles(_, stack):
  11215.         return (stack.getPile(),)
  11216.  
  11217.     
  11218.     def step010_movePile(_, r, pile, rows):
  11219.         lp = len(pile)
  11220.         lr = len(r.cards)
  11221.         if __debug__:
  11222.             if lp <= lp:
  11223.                 pass
  11224.             elif not lp <= lr:
  11225.                 raise AssertionError
  11226.         rpile = r.cards[:lr - lp]
  11227.         empty_row_seen = 0
  11228.         r_is_waste = r in _.game.sg.talonstacks
  11229.         for t in rows:
  11230.             (score, color) = (0, None)
  11231.             if r_is_waste:
  11232.                 (score, color) = _._getMoveWasteScore(score, color, r, t, pile, rpile)
  11233.             elif not (t.cards):
  11234.                 if lp == lr:
  11235.                     continue
  11236.                 
  11237.                 if empty_row_seen:
  11238.                     continue
  11239.                 
  11240.                 score = 60000
  11241.                 empty_row_seen = 1
  11242.             else:
  11243.                 score = 80000
  11244.             (score, color) = _._getMovePileScore(score, color, r, t, pile, rpile)
  11245.             _.addHint(score, lp, r, t, color)
  11246.         
  11247.  
  11248.     step020_getPiles = step010b_getPiles
  11249.     
  11250.     def step020(_, rows, foundations):
  11251.         for r in rows:
  11252.             for pile in _.step020_getPiles(r):
  11253.                 drop_info = []
  11254.                 i = 0
  11255.                 for c in pile:
  11256.                     rr = _.ClonedStack(r, stackcards = [
  11257.                         c])
  11258.                     (stack, ncards) = rr.canDropCards(foundations)
  11259.                     i = i + 1
  11260.                 
  11261.                 for di in drop_info:
  11262.                     c = di[0]
  11263.                     sub_pile = pile[di[3] + 1:]
  11264.                     if not __debug__ and r.canMoveCards(sub_pile):
  11265.                         raise AssertionError
  11266.                     0
  11267.                     for t in rows:
  11268.                         score = 40000
  11269.                         score = score + 1000 + (_.K - r.getCard().rank)
  11270.                         force = (999999, 0, di[2], r, di[1], _.BLUE, None)
  11271.                         _.addHint(score, len(sub_pile), r, t, _.RED, forced_move = force)
  11272.                     
  11273.                 
  11274.             
  11275.         
  11276.  
  11277.     
  11278.     def step030(_, foundations, rows, dropstacks):
  11279.         for s in foundations:
  11280.             card = s.getCard()
  11281.             for t in rows:
  11282.                 tt = _.ClonedStack(t, stackcards = t.cards + [
  11283.                     card])
  11284.                 for r in dropstacks:
  11285.                     pile = r.getPile()
  11286.                     if not pile:
  11287.                         continue
  11288.                     
  11289.                     if not tt.acceptsCards(r, pile):
  11290.                         continue
  11291.                     
  11292.                     rpile = r.cards[:len(r.cards) - len(pile)]
  11293.                     rr = _.ClonedStack(r, stackcards = rpile)
  11294.                     if rr.acceptsCards(t, pile):
  11295.                         continue
  11296.                     
  11297.                     score = 20000 + card.rank
  11298.                     force = (999999, 0, len(pile), r, t, _.BLUE, None)
  11299.                     _.addHint(score, 1, s, t, _.BLUE, forced_move = force)
  11300.                 
  11301.             
  11302.         
  11303.  
  11304.     
  11305.     def step040(_, rows, reservestacks):
  11306.         if not reservestacks:
  11307.             return None
  11308.         
  11309.         for r in rows:
  11310.             card = r.getCard()
  11311.             pile = [
  11312.                 card]
  11313.             rpile = r.cards[:len(r.cards) - len(pile)]
  11314.             rr = _.ClonedStack(r, stackcards = rpile)
  11315.             for t in reservestacks:
  11316.                 if rr.acceptsCards(t, pile):
  11317.                     continue
  11318.                 
  11319.                 score = 10000
  11320.                 (score, color) = _._getMovePileScore(score, None, r, t, pile, rpile)
  11321.                 _.addHint(score, len(pile), r, t, color)
  11322.             
  11323.         
  11324.  
  11325.     
  11326.     def step050(_, reservestacks, rows):
  11327.         if not reservestacks:
  11328.             return None
  11329.         
  11330.  
  11331.  
  11332.  
  11333. class CautiousDefaultHint(DefaultHint):
  11334.     shallMovePile = DefaultHint._cautiousShallMovePile
  11335.     
  11336.     def _preferHighRankMoves(_):
  11337.         return 1
  11338.  
  11339.  
  11340.  
  11341. class KlondikeType_Hint(DefaultHint):
  11342.     pass
  11343.  
  11344.  
  11345. class YukonType_Hint(CautiousDefaultHint):
  11346.     
  11347.     def step010b_getPiles(_, stack):
  11348.         p = stack.getPile()
  11349.         piles = []
  11350.         while p:
  11351.             piles.append(p)
  11352.             p = p[1:]
  11353.         return piles
  11354.  
  11355.  
  11356.  
  11357. class FreeCellType_Hint(CautiousDefaultHint):
  11358.     pass
  11359.  
  11360.  
  11361. class GolfType_Hint(DefaultHint):
  11362.     pass
  11363.  
  11364.  
  11365. class SpiderType_Hint(DefaultHint):
  11366.     pass
  11367.  
  11368.  
  11369. class Game:
  11370.     U_PLAY = 0
  11371.     U_WON = -2
  11372.     U_LOST = -3
  11373.     U_PERFECT = -4
  11374.     S_INIT = 0
  11375.     S_DEAL = 16
  11376.     S_FILL = 32
  11377.     S_PLAY = 48
  11378.     S_UNDO = 64
  11379.     S_REDO = 80
  11380.     GAME_VERSION = 1
  11381.     
  11382.     def __init__(_, gameinfo):
  11383.         _.preview = 0
  11384.         _.random = None
  11385.         _.gameinfo = gameinfo
  11386.         _.id = gameinfo.id
  11387.         if not __debug__ and _.id > 0:
  11388.             raise AssertionError
  11389.         _.busy = 0
  11390.         _.version = VERSION
  11391.         _.version_tuple = VERSION_TUPLE
  11392.         _.cards = []
  11393.         _.stackmap = { }
  11394.         _.allstacks = []
  11395.         _.demo_logo = None
  11396.         _.s = Struct(talon = None, waste = None, foundations = [], rows = [], reserves = [], internals = [])
  11397.         _.sg = Struct(openstacks = [], talonstacks = [], dropstacks = [], reservestacks = [], hp_stacks = [])
  11398.         _.regions = Struct(info = [], remaining = [], data = [])
  11399.         _.reset()
  11400.  
  11401.     
  11402.     def create(_, app):
  11403.         timer = Timer('Game.create')
  11404.         old_busy = _.busy
  11405.         _._Game__createCommon(app)
  11406.         _.setCursor(cursor = CURSOR_WATCH)
  11407.         _.top.wm_title(PACKAGE + ' - ' + _.getTitleName())
  11408.         _.top.wm_iconname(PACKAGE + ' - ' + _.getTitleName())
  11409.         if _.app.intro.progress:
  11410.             _.app.intro.progress.update(step = 1)
  11411.         
  11412.         _.createGame()
  11413.         _.sg.openstacks = filter((lambda s: s.cap.max_accept >= s.cap.min_accept), _.sg.openstacks)
  11414.         _.sg.hp_stacks = filter((lambda s: s.cap.max_move >= 2), _.sg.dropstacks)
  11415.         _.allstacks = tuple(_.allstacks)
  11416.         _.s.foundations = tuple(_.s.foundations)
  11417.         _.s.rows = tuple(_.s.rows)
  11418.         _.s.reserves = tuple(_.s.reserves)
  11419.         _.s.internals = tuple(_.s.internals)
  11420.         _.sg.openstacks = tuple(_.sg.openstacks)
  11421.         _.sg.talonstacks = tuple(_.sg.talonstacks)
  11422.         _.sg.dropstacks = tuple(_.sg.dropstacks)
  11423.         _.sg.reservestacks = tuple(_.sg.reservestacks)
  11424.         _.sg.hp_stacks = tuple(_.sg.hp_stacks)
  11425.         for stack in _.allstacks:
  11426.             stack.prepareStack()
  11427.             stack.assertStack()
  11428.         
  11429.         _.optimizeRegions()
  11430.         if not (_.cards):
  11431.             _.cards = _.createCards(progress = _.app.intro.progress)
  11432.         
  11433.         _.initBindings()
  11434.         _.top.wm_geometry('')
  11435.         _.canvas.config(width = _.width, height = _.height)
  11436.         _.stats.update_time = time.time()
  11437.         _.busy = old_busy
  11438.  
  11439.     
  11440.     def initBindings(_):
  11441.         bind(_.canvas, '<1>', _.clickHandler)
  11442.         bind(_.canvas, '<2>', _.clickHandler)
  11443.         bind(_.canvas, '<3>', _.clickHandler)
  11444.  
  11445.     
  11446.     def __createCommon(_, app):
  11447.         _.busy = 1
  11448.         _.app = app
  11449.         _.top = app.top
  11450.         _.canvas = app.canvas
  11451.         _.filename = ''
  11452.         _.drag = Struct(event = None, timer = None, start_x = 0, start_y = 0, stack = None, cards = [], shadows = [], shade_stack = None, shade_img = None, canshade_stacks = [], noshade_stacks = [])
  11453.         if _.gstats.start_player is None:
  11454.             _.gstats.start_player = _.app.opt.player
  11455.         
  11456.         _.texts = Struct(info = None, help = None, misc = None, score = None, base_rank = None)
  11457.  
  11458.     
  11459.     def createPreview(_, app):
  11460.         timer = Timer('Game.createPreview')
  11461.         old_busy = _.busy
  11462.         _._Game__createCommon(app)
  11463.         _.preview = max(1, _.canvas.preview)
  11464.         _.createGame()
  11465.         _.sg.openstacks = filter((lambda s: s.cap.max_accept >= s.cap.min_accept), _.sg.openstacks)
  11466.         _.sg.hp_stacks = filter((lambda s: s.cap.max_move >= 2), _.sg.dropstacks)
  11467.         for stack in _.allstacks:
  11468.             stack.prepareStack()
  11469.             stack.assertStack()
  11470.         
  11471.         _.optimizeRegions()
  11472.         _.cards = _.createCards()
  11473.         _.busy = old_busy
  11474.  
  11475.     
  11476.     def destruct(_):
  11477.         for obj in _.cards:
  11478.             destruct(obj)
  11479.         
  11480.         for obj in _.allstacks:
  11481.             obj.destruct()
  11482.             destruct(obj)
  11483.         
  11484.  
  11485.     
  11486.     def reset(_, restart = 0):
  11487.         _.filename = ''
  11488.         _.demo = None
  11489.         _.hints = Struct(list = None, index = -1, level = -1)
  11490.         _.saveinfo = Struct(stack_caps = [])
  11491.         _.loadinfo = Struct(stacks = None, talon_round = 1, ncards = 0)
  11492.         _.stats = Struct(hints = 0, highlight_piles = 0, highlight_cards = 0, highlight_samerank = 0, undo_moves = 0, redo_moves = 0, total_moves = 0, player_moves = 0, demo_moves = 0, autoplay_moves = 0, quickplay_moves = 0, goto_bookmark_moves = 0, demo_updated = 0, update_time = time.time(), elapsed_time = 0.0)
  11493.         _.startMoves()
  11494.         if restart:
  11495.             return None
  11496.         
  11497.         _.gstats = Struct(holded = 0, loaded = 0, saved = 0, restarted = 0, goto_bookmark_moves = 0, updated = _.U_PLAY, start_time = time.time(), total_elapsed_time = 0.0, start_player = None)
  11498.         _.gsaveinfo = Struct(bookmarks = { }, comment = '')
  11499.  
  11500.     
  11501.     def getTitleName(_):
  11502.         return _.app.getGameTitleName(_.id)
  11503.  
  11504.     
  11505.     def getGameNumber(_, format):
  11506.         s = str(_.random)
  11507.         if format:
  11508.             return '#' + s
  11509.         
  11510.         return s
  11511.  
  11512.     
  11513.     def setSize(_, w, h):
  11514.         (_.width, _.height) = (int(round(w)), int(round(h)))
  11515.  
  11516.     
  11517.     def setCursor(_, cursor):
  11518.         if _.canvas:
  11519.             _.canvas.config(cursor = cursor)
  11520.         
  11521.         if _.app and _.app.toolbar:
  11522.             _.app.toolbar.setCursor(cursor = cursor)
  11523.         
  11524.  
  11525.     
  11526.     def newGame(_, random = None, restart = 0, autoplay = 1):
  11527.         (old_busy, _.busy) = (_.busy, 1)
  11528.         _.setCursor(cursor = CURSOR_WATCH)
  11529.         _.disableMenus()
  11530.         _.reset(restart = restart)
  11531.         _.resetGame()
  11532.         _.createRandom(random)
  11533.         _.shuffle()
  11534.         if not __debug__ and len(_.s.talon.cards) == _.gameinfo.ncards:
  11535.             raise AssertionError
  11536.         for stack in _.allstacks:
  11537.             stack.updateText()
  11538.         
  11539.         _.updateText()
  11540.         _.updateStatus(player = _.app.opt.player)
  11541.         _.updateStatus(gamenumber = _.getGameNumber(format = 1), moves = 0)
  11542.         _.updateStatus(stats = _.app.stats.getStats(_.app.opt.player, _.id))
  11543.         _.stopSamples()
  11544.         _.moves.state = _.S_INIT
  11545.         _.startGame()
  11546.         if _.gameinfo.si.game_flags & GI.GT_OPEN:
  11547.             if _.s.talon:
  11548.                 if not __debug__ and len(_.s.talon.cards) == 0:
  11549.                     raise AssertionError
  11550.                 None if not (_.preview) else _.allstacks
  11551.             
  11552.         
  11553.         _.startMoves()
  11554.         for stack in _.allstacks:
  11555.             stack.updateText()
  11556.         
  11557.         _.updateText()
  11558.         _.updateStatus(moves = 0)
  11559.         _.updateMenus()
  11560.         _.stopSamples()
  11561.         _.setCursor(cursor = _.app.top_cursor)
  11562.         _.stats.update_time = time.time()
  11563.         _.busy = old_busy
  11564.  
  11565.     
  11566.     def restoreGame(_, game, reset = 1):
  11567.         (old_busy, _.busy) = (_.busy, 1)
  11568.         if reset:
  11569.             _.reset()
  11570.         
  11571.         _.resetGame()
  11572.         _.filename = game.filename
  11573.         _.version = game.version
  11574.         _.version_tuple = game.version_tuple
  11575.         _.random = game.random
  11576.         _.moves = game.moves
  11577.         _.stats = game.stats
  11578.         _.gstats = game.gstats
  11579.         _.saveinfo = game.saveinfo
  11580.         _.gsaveinfo = game.gsaveinfo
  11581.         _.s.talon.round = game.loadinfo.talon_round
  11582.         if not __debug__ and len(_.allstacks) == len(game.loadinfo.stacks):
  11583.             raise AssertionError
  11584.         for i in range(len(_.allstacks)):
  11585.             for t in game.loadinfo.stacks[i]:
  11586.                 (card_id, face_up) = t
  11587.                 card = _.cards[card_id]
  11588.                 _.allstacks[i].addCard(card)
  11589.             
  11590.         
  11591.         for stack_id, cap in _.saveinfo.stack_caps:
  11592.             _.allstacks[stack_id].cap.update(cap.__dict__)
  11593.         
  11594.         _._restoreGameHook(game)
  11595.         for stack in _.allstacks:
  11596.             stack.updateText()
  11597.         
  11598.         _.updateText()
  11599.         _.updateStatus(player = _.app.opt.player)
  11600.         _.updateStatus(gamenumber = _.getGameNumber(format = 1), moves = _.moves.index)
  11601.         _.updateStatus(stats = _.app.stats.getStats(_.app.opt.player, _.id))
  11602.         _.setCursor(cursor = _.app.top_cursor)
  11603.         _.stats.update_time = time.time()
  11604.         _.busy = old_busy
  11605.  
  11606.     
  11607.     def restoreGameFromBookmark(_, bookmark):
  11608.         (old_busy, _.busy) = (_.busy, 1)
  11609.         file = StringIO.StringIO(bookmark)
  11610.         p = Unpickler(file)
  11611.         game = _._undumpGame(p, _.app)
  11612.         _.restoreGame(game, reset = 0)
  11613.         destruct(game)
  11614.         _.busy = old_busy
  11615.  
  11616.     
  11617.     def resetGame(_):
  11618.         _.hints.list = None
  11619.         _.s.talon.removeAllCards()
  11620.         for stack in _.allstacks:
  11621.             stack.resetGame()
  11622.         
  11623.  
  11624.     
  11625.     def nextGameFlags(_, id, random = None):
  11626.         f = 0
  11627.         if id != _.id:
  11628.             f = f | 1
  11629.         
  11630.         if _.app.nextgame.cardset is not _.app.cardset:
  11631.             f = f | 2
  11632.         
  11633.         if random is not None:
  11634.             if random.__class__ is not _.random.__class__:
  11635.                 f = f | 16
  11636.             elif random.initial_seed != _.random.initial_seed:
  11637.                 f = f | 16
  11638.             
  11639.         
  11640.         return f
  11641.  
  11642.     
  11643.     def quitGame(_, id = 0, random = None, loadedgame = None, startdemo = 0, bookmark = 0, holdgame = 0):
  11644.         _.updateTime()
  11645.         if bookmark:
  11646.             (id, random) = (_.id, _.random)
  11647.             file = StringIO.StringIO()
  11648.             p = Pickler(file, 1)
  11649.             _._dumpGame(p, bookmark = 1)
  11650.             _.app.nextgame.bookmark = file.getvalue()
  11651.         
  11652.         if id > 0:
  11653.             _.setCursor(cursor = CURSOR_WATCH)
  11654.         
  11655.         _.app.nextgame.id = id
  11656.         _.app.nextgame.random = random
  11657.         _.app.nextgame.loadedgame = loadedgame
  11658.         _.app.nextgame.startdemo = startdemo
  11659.         _.app.nextgame.holdgame = holdgame
  11660.         _.updateStatus(moves = None, gamenumber = None, stats = None)
  11661.         _.top.mainquit()
  11662.  
  11663.     
  11664.     def endGame(_, restart = 0, bookmark = 0, holdgame = 0):
  11665.         if _.preview:
  11666.             return None
  11667.         
  11668.         _.app.wm_save_state()
  11669.         if holdgame:
  11670.             return None
  11671.         
  11672.         if bookmark:
  11673.             return None
  11674.         
  11675.         if restart:
  11676.             if _.moves.index > 0 and _.getPlayerMoves() > 0:
  11677.                 _.gstats.restarted = _.gstats.restarted + 1
  11678.             
  11679.             return None
  11680.         
  11681.         _.updateStats()
  11682.         stats = _.app.stats
  11683.         if _.shallUpdateBalance():
  11684.             b = _.getGameBalance()
  11685.             if b:
  11686.                 stats.total_balance[_.id] = stats.total_balance.get(_.id, 0) + b
  11687.                 stats.session_balance[_.id] = stats.session_balance.get(_.id, 0) + b
  11688.                 stats.gameid_balance = stats.gameid_balance + b
  11689.             
  11690.         
  11691.  
  11692.     
  11693.     def restartGame(_):
  11694.         _.endGame(restart = 1)
  11695.         _.newGame(restart = 1, random = _.random)
  11696.  
  11697.     
  11698.     def createRandom(_, random):
  11699.         if random is None:
  11700.             if isinstance(_.random, LCRandom64):
  11701.                 seed = _.random.getSeed()
  11702.                 _.app.gamerandom.setSeed(seed)
  11703.             
  11704.             while 1:
  11705.                 dummy = _.app.gamerandom.random()
  11706.                 seed = _.app.gamerandom.getSeed()
  11707.                 if seed >= 0x2386F26FC10000L:
  11708.                     break
  11709.                 
  11710.             _.random = LCRandom64(seed)
  11711.             _.random.origin = _.random.ORIGIN_RANDOM
  11712.         else:
  11713.             _.random = random
  11714.             _.random.reset()
  11715.  
  11716.     
  11717.     def enterState(_, state):
  11718.         old_state = _.moves.state
  11719.         if state < old_state:
  11720.             _.moves.state = state
  11721.         
  11722.         return old_state
  11723.  
  11724.     
  11725.     def leaveState(_, old_state):
  11726.         _.moves.state = old_state
  11727.  
  11728.     
  11729.     def createCards(_, progress = None):
  11730.         timer = Timer('Game.createCards')
  11731.         gi = _.gameinfo
  11732.         pstep = 0
  11733.         if progress:
  11734.             pstep = (100.0 - progress.percent) / gi.ncards
  11735.         
  11736.         cards = []
  11737.         id = 0
  11738.         (x, y) = (_.s.talon.x, _.s.talon.y)
  11739.         for deck in range(gi.decks):
  11740.             for suit in gi.suits:
  11741.                 for rank in gi.ranks:
  11742.                     card = _._createCard(id, deck, suit, rank, x = x, y = y)
  11743.                     cards.append(card)
  11744.                     id = id + 1
  11745.                 
  11746.             
  11747.             trump_suit = len(gi.suits)
  11748.             for rank in gi.trumps:
  11749.                 card = _._createCard(id, deck, trump_suit, rank, x = x, y = y)
  11750.                 cards.append(card)
  11751.                 id = id + 1
  11752.             
  11753.         
  11754.         if progress:
  11755.             progress.update(percent = 100)
  11756.         
  11757.         if not __debug__ and len(cards) == gi.ncards:
  11758.             raise AssertionError
  11759.         return cards
  11760.  
  11761.     
  11762.     def _createCard(_, id, deck, suit, rank, x, y):
  11763.         return Card(id, deck, suit, rank, game = _, x = x, y = y)
  11764.  
  11765.     
  11766.     def shuffle(_):
  11767.         cards = list(_.cards)[:]
  11768.         _.random.reset()
  11769.         _.random.shuffle(cards)
  11770.         cards = _._shuffleHook(cards)
  11771.         for card in cards:
  11772.             _.s.talon.addCard(card, update = 0)
  11773.             card.showBack(unhide = 0)
  11774.         
  11775.  
  11776.     
  11777.     def shuffleSeparateDecks(_):
  11778.         cards = []
  11779.         _.random.reset()
  11780.         n = _.gameinfo.ncards / _.gameinfo.decks
  11781.         for deck in range(_.gameinfo.decks):
  11782.             i = deck * n
  11783.             deck_cards = list(_.cards)[i:i + n]
  11784.             _.random.shuffle(deck_cards)
  11785.             cards.extend(deck_cards)
  11786.         
  11787.         cards = _._shuffleHook(cards)
  11788.         for card in cards:
  11789.             _.s.talon.addCard(card, update = 0)
  11790.             card.showBack(unhide = 0)
  11791.         
  11792.  
  11793.     
  11794.     def _shuffleHook(_, cards):
  11795.         return cards
  11796.  
  11797.     
  11798.     def _shuffleHookMoveToTop(_, cards, func, ncards = 999999):
  11799.         (cards, scards) = _._shuffleHookMoveSorter(cards, func, ncards)
  11800.         return cards + scards
  11801.  
  11802.     
  11803.     def _shuffleHookMoveToBottom(_, cards, func, ncards = 999999):
  11804.         (cards, scards) = _._shuffleHookMoveSorter(cards, func, ncards)
  11805.         return scards + cards
  11806.  
  11807.     
  11808.     def _shuffleHookMoveSorter(_, cards, func, ncards):
  11809.         (sitems, i) = ([], len(cards))
  11810.         for c in cards[:]:
  11811.             (select, sort_order) = func(c)
  11812.             if select:
  11813.                 cards.remove(c)
  11814.                 sitems.append((sort_order, i, c))
  11815.                 if len(sitems) >= ncards:
  11816.                     break
  11817.                 
  11818.             
  11819.             i = i - 1
  11820.         
  11821.         sitems.sort()
  11822.         sitems.reverse()
  11823.         scards = map((lambda item: item[2]), sitems)
  11824.         return (cards, scards)
  11825.  
  11826.     
  11827.     def _finishDrag(_):
  11828.         if _.demo:
  11829.             _.stopDemo()
  11830.         
  11831.         if _.busy:
  11832.             return 1
  11833.         
  11834.         if _.drag.stack:
  11835.             _.drag.stack.finishDrag()
  11836.         
  11837.         return 0
  11838.  
  11839.     
  11840.     def _cancelDrag(_):
  11841.         if _.demo:
  11842.             _.stopDemo()
  11843.         
  11844.         if _.busy:
  11845.             return 1
  11846.         
  11847.         if _.drag.stack:
  11848.             _.drag.stack.cancelDrag()
  11849.         
  11850.         return 0
  11851.  
  11852.     
  11853.     def updateMenus(_):
  11854.         if not (_.preview):
  11855.             _.app.menubar.updateMenus()
  11856.         
  11857.  
  11858.     
  11859.     def disableMenus(_):
  11860.         if not (_.preview):
  11861.             _.app.menubar.disableMenus()
  11862.         
  11863.  
  11864.     
  11865.     def clickHandler(_, *args):
  11866.         if _.demo:
  11867.             _.stopDemo()
  11868.         
  11869.         return EVENT_PROPAGATE
  11870.  
  11871.     
  11872.     def updateStatus(_, **kw):
  11873.         if _.preview:
  11874.             return None
  11875.         
  11876.         (tb, sb) = (_.app.toolbar, _.app.statusbar)
  11877.         for k, v in kw.items():
  11878.             if k == 'gamenumber':
  11879.                 if v is None:
  11880.                     continue
  11881.                 
  11882.                 if type(v) is types.StringType:
  11883.                     if sb:
  11884.                         sb.updateText(gamenumber = v)
  11885.                     
  11886.                     continue
  11887.                 
  11888.             
  11889.             if k == 'info':
  11890.                 if v is None:
  11891.                     if sb:
  11892.                         sb.updateText(info = '')
  11893.                     
  11894.                     continue
  11895.                 
  11896.                 if type(v) is types.StringType:
  11897.                     if sb:
  11898.                         sb.updateText(info = v)
  11899.                     
  11900.                     continue
  11901.                 
  11902.             
  11903.             if k == 'moves':
  11904.                 if v is None:
  11905.                     if sb:
  11906.                         sb.updateText(moves = '')
  11907.                     
  11908.                     continue
  11909.                 
  11910.                 if type(v) is types.IntType:
  11911.                     if sb:
  11912.                         sb.updateText(moves = 'Moves %d' % v)
  11913.                     
  11914.                     continue
  11915.                 
  11916.                 if type(v) is types.StringType:
  11917.                     if sb:
  11918.                         sb.updateText(moves = v)
  11919.                     
  11920.                     continue
  11921.                 
  11922.             
  11923.             if k == 'player':
  11924.                 if v is None:
  11925.                     if tb:
  11926.                         tb.updateText(player = 'Player\n')
  11927.                     
  11928.                     continue
  11929.                 
  11930.                 if type(v) is types.StringType:
  11931.                     if tb:
  11932.                         if _.app.opt.toolbar_size:
  11933.                             tb.updateText(player = 'Player\n' + v)
  11934.                         else:
  11935.                             tb.updateText(player = v)
  11936.                     
  11937.                     continue
  11938.                 
  11939.             
  11940.             if k == 'stats':
  11941.                 if v is None:
  11942.                     if sb:
  11943.                         sb.updateText(stats = '')
  11944.                     
  11945.                     continue
  11946.                 
  11947.                 if type(v) is types.TupleType:
  11948.                     t = '%d: %d/%d' % (v[0] + v[1], v[0], v[1])
  11949.                     if sb:
  11950.                         sb.updateText(stats = t)
  11951.                     
  11952.                     continue
  11953.                 
  11954.             
  11955.             if k == 'time':
  11956.                 continue
  11957.             
  11958.             raise AttributeError, k
  11959.         
  11960.  
  11961.     
  11962.     def playSample(_, name, priority = 0, loop = 0):
  11963.         if _.app.audio:
  11964.             return _.app.audio.playSample(name, priority = priority, loop = loop)
  11965.         
  11966.         return 0
  11967.  
  11968.     
  11969.     def stopSamples(_):
  11970.         if _.app.audio:
  11971.             _.app.audio.stopSamples()
  11972.         
  11973.  
  11974.     
  11975.     def stopSamplesLoop(_):
  11976.         if _.app.audio:
  11977.             _.app.audio.stopSamplesLoop()
  11978.         
  11979.  
  11980.     
  11981.     def startDealSample(_):
  11982.         a = _.app.opt.animations
  11983.         if a and not (_.preview):
  11984.             _.canvas.update_idletasks()
  11985.         
  11986.         if _.app.audio and _.app.opt.sound:
  11987.             if a in (1, 2, 5):
  11988.                 _.playSample('deal01', priority = 100, loop = 1)
  11989.             elif a == 3:
  11990.                 _.playSample('deal04', priority = 100, loop = 1)
  11991.             elif a == 4:
  11992.                 _.playSample('deal08', priority = 100, loop = 1)
  11993.             
  11994.         
  11995.  
  11996.     
  11997.     def areYouSure(_, title = None, text = None, confirm = -1, default = 0):
  11998.         if _.preview:
  11999.             return 1
  12000.         
  12001.         if confirm < 0:
  12002.             confirm = _.app.opt.confirm
  12003.         
  12004.         if confirm:
  12005.             if not title:
  12006.                 title = PACKAGE
  12007.             
  12008.             if not text:
  12009.                 text = 'Discard current game ?'
  12010.             
  12011.             _.playSample('areyousure')
  12012.             d = MfxDialog(_.top, title = title, text = text, bitmap = 'questhead', default = default, strings = ('OK', 'Cancel'))
  12013.             if d.status != 0 or d.button != 0:
  12014.                 return 0
  12015.             
  12016.         
  12017.         return 1
  12018.  
  12019.     
  12020.     def notYetImplemented(_):
  12021.         d = MfxDialog(_.top, title = 'Not yet implemented', text = 'This function is\nnot yet implemented.', bitmap = 'error')
  12022.  
  12023.     
  12024.     def animatedMoveTo(_, from_stack, to_stack, cards, x, y, tkraise = 1, frames = -1, shadow = -1):
  12025.         if _.app.opt.animations == 0 or frames == 0:
  12026.             return None
  12027.         
  12028.         if _.app.debug and not _.top.winfo_ismapped():
  12029.             return None
  12030.         
  12031.         (clock, delay, skip) = (None, 1, 1)
  12032.         if _.app.opt.animations >= 2:
  12033.             clock = uclock
  12034.         
  12035.         SPF = 0.15 / 8
  12036.         if frames < 0:
  12037.             frames = 8
  12038.         
  12039.         if not __debug__ and frames >= 2:
  12040.             raise AssertionError
  12041.         if _.app.opt.animations == 3:
  12042.             frames = frames * 8
  12043.             SPF = SPF / 2
  12044.         elif _.app.opt.animations == 4:
  12045.             frames = frames * 16
  12046.             SPF = SPF / 2
  12047.         elif _.app.opt.animations == 5:
  12048.             if _.moves.state == _.S_INIT and frames > 4:
  12049.                 frames = frames / 2
  12050.             
  12051.         
  12052.         if shadow < 0:
  12053.             shadow = _.app.opt.shadow
  12054.         
  12055.         shadows = ()
  12056.         c0 = cards[0]
  12057.         (dx, dy) = ((x - c0.x) / float(frames), (y - c0.y) / float(frames))
  12058.         (tx, ty) = (0, 0)
  12059.         i = 1
  12060.         if clock:
  12061.             starttime = clock()
  12062.         
  12063.         while i < frames:
  12064.             (mx, my) = (int(round(dx * i)) - tx, int(round(dy * i)) - ty)
  12065.             (tx, ty) = (tx + mx, ty + my)
  12066.             for s in shadows:
  12067.                 s.move(mx, my)
  12068.             
  12069.             for card in cards:
  12070.                 card.moveBy(mx, my)
  12071.             
  12072.             _.canvas.update_idletasks()
  12073.             step = 1
  12074.             i = i + step
  12075.             continue
  12076.             None if clock else shadows
  12077.         for s in shadows:
  12078.             s.delete()
  12079.         
  12080.         (dx, dy) = (x - c0.x, y - c0.y)
  12081.         for card in cards:
  12082.             card.moveBy(dx, dy)
  12083.         
  12084.         _.canvas.update_idletasks()
  12085.  
  12086.     
  12087.     def winAnimation(_, perfect = 0):
  12088.         if not (_.app.opt.animations):
  12089.             return None
  12090.         
  12091.         if _.app.debug and not _.top.winfo_ismapped():
  12092.             return None
  12093.         
  12094.         old_a = _.app.opt.animations
  12095.         if old_a == 0:
  12096.             _.app.opt.animations = 1
  12097.         elif old_a == 4:
  12098.             _.app.opt.animations = 3
  12099.         
  12100.         cards = []
  12101.         for s in _.allstacks:
  12102.             pass
  12103.         
  12104.         acards = []
  12105.         for i in range(16):
  12106.             (c, s) = _.app.miscrandom.choice(cards)
  12107.         
  12108.         (sx, sy) = (_.s.talon.x, _.s.talon.y)
  12109.         (w, h) = (_.width, _.height)
  12110.         while cards:
  12111.             t = _.app.miscrandom.choice(cards)
  12112.             (c, s) = t
  12113.             s.removeCard(c, update = 0)
  12114.             cards.remove(t)
  12115.             continue
  12116.             None if c in acards or len(cards) <= 2 else _.allstacks
  12117.         _.app.opt.animations = old_a
  12118.  
  12119.     
  12120.     def sleep(_, seconds):
  12121.         if seconds > 0:
  12122.             if _.top:
  12123.                 _.top.sleep(seconds)
  12124.             else:
  12125.                 time.sleep(seconds)
  12126.         
  12127.  
  12128.     
  12129.     def getCardFaceImage(_, deck, suit, rank):
  12130.         if _.app.cardset.type == CSI.TYPE_TAROCK:
  12131.             if rank >= 10:
  12132.                 rank = rank + 1
  12133.             
  12134.         
  12135.         return _.app.images.getFace(deck, suit, rank)
  12136.  
  12137.     
  12138.     def getCardBackImage(_, deck, suit, rank):
  12139.         return _.app.images.getBack(deck, suit, rank)
  12140.  
  12141.     
  12142.     def _getClosestStack(_, cx, cy, stacks, dragstack):
  12143.         (closest, cdist) = (None, 999999999)
  12144.         for stack in stacks:
  12145.             dist = (stack.x - cx) ** 2 + (stack.y - cy) ** 2
  12146.         
  12147.         return closest
  12148.  
  12149.     
  12150.     def getClosestStack(_, card, dragstack):
  12151.         (cx, cy) = (card.x, card.y)
  12152.         for stacks, rect in _.regions.info:
  12153.             pass
  12154.         
  12155.         return _._getClosestStack(cx, cy, _.regions.remaining, dragstack)
  12156.  
  12157.     
  12158.     def setRegion(_, stacks, rect, priority = 0):
  12159.         if not __debug__ and len(stacks) > 0:
  12160.             raise AssertionError
  12161.         if __debug__:
  12162.             if not len(rect) == 4 and rect[0] < rect[2] and rect[1] < rect[3]:
  12163.                 raise AssertionError
  12164.         for s in stacks:
  12165.             if __debug__:
  12166.                 if not s and s in _.allstacks:
  12167.                     raise AssertionError
  12168.             0
  12169.             (x, y, r) = (s.x, s.y, rect)
  12170.             if __debug__:
  12171.                 if not x >= r[0] and x < r[2] and y >= r[1] and y < r[3]:
  12172.                     raise AssertionError
  12173.             stacks
  12174.             for d in _.regions.data:
  12175.                 if priority == d[0]:
  12176.                     if not __debug__ and not (s in d[2]):
  12177.                         raise AssertionError
  12178.                     0
  12179.                 
  12180.             
  12181.         
  12182.         _.regions.data.append((priority, -len(_.regions.data), tuple(stacks), tuple(rect)))
  12183.  
  12184.     
  12185.     def optimizeRegions(_):
  12186.         _.regions.data.sort()
  12187.         _.regions.data.reverse()
  12188.         _.regions.info = []
  12189.         for d in _.regions.data:
  12190.             _.regions.info.append((d[2], d[3]))
  12191.         
  12192.         _.regions.info = tuple(_.regions.info)
  12193.         remaining = list(_.sg.openstacks)[:]
  12194.         for stacks, rect in _.regions.info:
  12195.             for stack in stacks:
  12196.                 while stack in remaining:
  12197.                     remaining.remove(stack)
  12198.                     continue
  12199.                     0
  12200.             
  12201.         
  12202.         _.regions.remaining = tuple(remaining)
  12203.  
  12204.     
  12205.     def createGame(_):
  12206.         raise SubclassResponsibility
  12207.  
  12208.     
  12209.     def startGame(_):
  12210.         raise SubclassResponsibility
  12211.  
  12212.     
  12213.     def canDealCards(_):
  12214.         if _.s.talon:
  12215.             pass
  12216.         return _.s.talon.canDealCards()
  12217.  
  12218.     
  12219.     def dealCards(_, sound = 1):
  12220.         if _.s.talon and _.canDealCards():
  12221.             _.finishMove()
  12222.             old_state = _.enterState(_.S_DEAL)
  12223.             n = _.s.talon.dealCards(sound = sound)
  12224.             _.leaveState(old_state)
  12225.             _.finishMove()
  12226.             if not _.checkForWin():
  12227.                 _.autoPlay()
  12228.             
  12229.             return n
  12230.         
  12231.         return 0
  12232.  
  12233.     
  12234.     def fillStack(_, stack):
  12235.         pass
  12236.  
  12237.     Hint_Class = DefaultHint
  12238.     
  12239.     def getHintClass(_):
  12240.         return _.Hint_Class
  12241.  
  12242.     
  12243.     def getStrictness(_):
  12244.         return 0
  12245.  
  12246.     
  12247.     def canSaveGame(_):
  12248.         return 1
  12249.  
  12250.     
  12251.     def canLoadGame(_, version_tuple, game_version):
  12252.         return _.GAME_VERSION == game_version
  12253.  
  12254.     
  12255.     def canSetBookmark(_):
  12256.         return _.canSaveGame()
  12257.  
  12258.     
  12259.     def canUndo(_):
  12260.         return 1
  12261.  
  12262.     
  12263.     def canRedo(_):
  12264.         return _.canUndo()
  12265.  
  12266.     
  12267.     def changed(_, restart = 0):
  12268.         if _.gstats.updated < 0:
  12269.             return 0
  12270.         
  12271.         if _.gstats.loaded > 0:
  12272.             return 0
  12273.         
  12274.         if not restart:
  12275.             if _.gstats.restarted > 0:
  12276.                 return 1
  12277.             
  12278.             if _.gstats.goto_bookmark_moves > 0:
  12279.                 return 1
  12280.             
  12281.         
  12282.         if _.moves.index == 0 or _.getPlayerMoves() == 0:
  12283.             return 0
  12284.         
  12285.         return 2
  12286.  
  12287.     
  12288.     def getWinStatus(_):
  12289.         won = _.isGameWon() != 0
  12290.         if not won and _.stats.hints > 0 or _.stats.demo_moves > 0:
  12291.             return (won, 0, _.U_LOST)
  12292.         
  12293.         if _.stats.undo_moves == 0 and _.stats.goto_bookmark_moves == 0 and _.stats.highlight_piles == 0 and _.stats.highlight_cards == 0:
  12294.             return (won, 2, _.U_PERFECT)
  12295.         
  12296.         return (won, 1, _.U_WON)
  12297.  
  12298.     
  12299.     def updateStats(_, demo = 0):
  12300.         (won, status, updated) = _.getWinStatus()
  12301.         if demo and _.getPlayerMoves() == 0:
  12302.             _.stats.demo_updated = updated
  12303.             _.app.stats.updateStats(None, _, won)
  12304.         elif _.changed():
  12305.             _.gstats.updated = updated
  12306.             if _.app.opt.update_player_stats:
  12307.                 _.app.stats.updateStats(_.app.opt.player, _, status)
  12308.                 _.updateStatus(stats = _.app.stats.getStats(_.app.opt.player, _.id))
  12309.             
  12310.         elif not demo:
  12311.             if _.app.opt.update_player_stats:
  12312.                 if _.gstats.loaded:
  12313.                     _.app.stats.updateLog(_.app.opt.player, _, -2)
  12314.                 elif _.gstats.updated == 0 and _.stats.demo_updated == 0:
  12315.                     _.app.stats.updateLog(_.app.opt.player, _, -1)
  12316.                 
  12317.             
  12318.         
  12319.  
  12320.     
  12321.     def checkForWin(_):
  12322.         (won, status, updated) = _.getWinStatus()
  12323.         if not won:
  12324.             return 0
  12325.         
  12326.         _.finishMove()
  12327.         if _.preview:
  12328.             return 1
  12329.         
  12330.         if status == 2:
  12331.             _.updateStats()
  12332.             _.playSample('winperfect', priority = 1000)
  12333.             d = MfxDialog(_.top, title = 'Game won', text = '\nCongratulations, this\nwas a truly perfect game !\n\n' + 'Your playing time is ' + _.getTime() + '\n' + 'for ' + str(_.moves.index) + ' moves.\n', strings = ('New game', None, 'Cancel'), image = _.app.gimages.logos[5], separatorwidth = 2)
  12334.         elif status == 1:
  12335.             _.updateStats()
  12336.             _.playSample('winwon', priority = 1000)
  12337.             d = MfxDialog(_.top, title = 'Game won', text = '\nCongratulations, you did it !\n\n' + 'Your playing time is ' + _.getTime() + '\n' + 'for ' + str(_.moves.index) + ' moves.\n', strings = ('New game', None, 'Cancel'), image = _.app.gimages.logos[4], separatorwidth = 2)
  12338.         elif _.gstats.updated < 0:
  12339.             _.playSample('winfinished', priority = 1000)
  12340.             d = MfxDialog(_.top, title = 'Game finished', text = '\nGame finished\n', bitmap = '', strings = ('New game', None, 'Cancel'))
  12341.         else:
  12342.             _.playSample('winlost', priority = 1000)
  12343.             d = MfxDialog(_.top, title = 'Game finished', text = '\nGame finished, but not without my help...\n', bitmap = '', strings = ('New game', 'Restart', 'Cancel'))
  12344.         if d.status == 0 and d.button == 0:
  12345.             _.endGame()
  12346.             if status == 2:
  12347.                 _.winAnimation(perfect = 1)
  12348.             elif status == 1:
  12349.                 _.winAnimation()
  12350.             
  12351.             _.newGame()
  12352.         elif d.status == 0 and d.button == 1:
  12353.             _.restartGame()
  12354.         
  12355.         return 1
  12356.  
  12357.     
  12358.     def isGameWon(_):
  12359.         c = 0
  12360.         for s in _.s.foundations:
  12361.             c = c + len(s.cards)
  12362.         
  12363.         return c == len(_.cards)
  12364.  
  12365.     
  12366.     def getFoundationDir(_):
  12367.         for s in _.s.foundations:
  12368.             pass
  12369.         
  12370.         return 0
  12371.  
  12372.     
  12373.     def getPlayerMoves(_):
  12374.         player_moves = _.stats.player_moves
  12375.         return player_moves
  12376.  
  12377.     
  12378.     def updateTime(_):
  12379.         t = time.time()
  12380.         d = t - _.stats.update_time
  12381.         if d > 0:
  12382.             _.stats.elapsed_time = _.stats.elapsed_time + d
  12383.             _.gstats.total_elapsed_time = _.gstats.total_elapsed_time + d
  12384.         
  12385.         _.stats.update_time = t
  12386.  
  12387.     
  12388.     def getTime(_):
  12389.         _.updateTime()
  12390.         t = int(round(_.stats.elapsed_time))
  12391.         if t <= 0:
  12392.             return '0:00'
  12393.         
  12394.         if t < 3600:
  12395.             return '%d:%02d' % (t / 60, t % 60)
  12396.         
  12397.         return '%d:%02d:%02d' % (t / 3600, (t % 3600) / 60, t % 60)
  12398.  
  12399.     
  12400.     def getAutoStacks(_, event = None):
  12401.         return (_.sg.dropstacks, _.sg.dropstacks, _.sg.dropstacks)
  12402.  
  12403.     
  12404.     def autoPlay(_, autofaceup = -1, autodrop = -1, autodeal = -1, sound = 1):
  12405.         if _.demo:
  12406.             return 0
  12407.         
  12408.         (old_busy, _.busy) = (_.busy, 1)
  12409.         if autofaceup < 0:
  12410.             autofaceup = _.app.opt.autofaceup
  12411.         
  12412.         if autodrop < 0:
  12413.             autodrop = _.app.opt.autodrop
  12414.         
  12415.         if autodeal < 0:
  12416.             autodeal = _.app.opt.autodeal
  12417.         
  12418.         moves = _.stats.total_moves
  12419.         n = _._autoPlay(autofaceup, autodrop, autodeal, sound = sound)
  12420.         _.finishMove()
  12421.         _.stats.autoplay_moves = _.stats.autoplay_moves + (_.stats.total_moves - moves)
  12422.         _.busy = old_busy
  12423.         return n
  12424.  
  12425.     
  12426.     def _autoPlay(_, autofaceup, autodrop, autodeal, sound):
  12427.         (flipstacks, dropstacks, quickstacks) = _.getAutoStacks()
  12428.         done_something = 1
  12429.         while done_something:
  12430.             done_something = 0
  12431.             if autofaceup and flipstacks:
  12432.                 for s in flipstacks:
  12433.                     if s.canFlipCard():
  12434.                         s.flipMove()
  12435.                         done_something = 1
  12436.                         _.finishMove()
  12437.                         if _.checkForWin():
  12438.                             return 1
  12439.                         
  12440.                     
  12441.                 
  12442.             
  12443.             if autodrop and dropstacks:
  12444.                 for s in dropstacks:
  12445.                     (to_stack, ncards) = s.canDropCards(_.s.foundations)
  12446.                     if to_stack:
  12447.                         _.finishMove()
  12448.                         s.moveMove(ncards, to_stack)
  12449.                         done_something = 1
  12450.                         if _.checkForWin():
  12451.                             return 1
  12452.                         
  12453.                     
  12454.                 
  12455.             
  12456.             if autodeal:
  12457.                 if _._autoDeal(sound = sound):
  12458.                     done_something = 1
  12459.                     _.finishMove()
  12460.                     if _.checkForWin():
  12461.                         return 1
  12462.                     
  12463.                 
  12464.             
  12465.         return 0
  12466.  
  12467.     
  12468.     def _autoDeal(_, sound = 1):
  12469.         w = _.s.waste
  12470.         if w and len(w.cards) == 0 and _.canDealCards():
  12471.             return _.dealCards(sound = sound)
  12472.         
  12473.         return 0
  12474.  
  12475.     
  12476.     def getHighlightPilesStacks(_):
  12477.         if _.sg.hp_stacks:
  12478.             return ((_.sg.hp_stacks, 2),)
  12479.         
  12480.         return ()
  12481.  
  12482.     
  12483.     def _highlightCards(_, info, sleep = 1.5):
  12484.         if not info:
  12485.             return 0
  12486.         
  12487.         items = []
  12488.         for s, c1, c2, color in info:
  12489.             if __debug__:
  12490.                 if not c1 in s.cards and c2 in s.cards:
  12491.                     raise AssertionError
  12492.             0
  12493.             sy0 = s.CARD_YOFFSET[0]
  12494.             if sy0 >= 0:
  12495.                 (x1, y1) = s.getPositionFor(c1)
  12496.                 (x2, y2) = s.getPositionFor(c2)
  12497.                 if c2 is not s.cards[-1] and sy0 > 0:
  12498.                     y2 = y2 + sy0
  12499.                 else:
  12500.                     y2 = y2 + _.app.images.CARDH
  12501.             else:
  12502.                 (x1, y1) = s.getPositionFor(c2)
  12503.                 (x2, y2) = s.getPositionFor(c1)
  12504.                 y2 = y2 + _.app.images.CARDH
  12505.                 if c2 is not s.cards[-1]:
  12506.                     y1 = y1 + _.app.images.CARDH + sy0
  12507.                 
  12508.             x2 = x2 + _.app.images.CARDW
  12509.             r = MfxCanvasRectangle(_.canvas, x1 - 1, y1 - 1, x2 + 1, y2 + 1, width = 4, fill = None, outline = color)
  12510.             r.tkraise(c2.item)
  12511.             items.append(r)
  12512.         
  12513.         if not items:
  12514.             return 0
  12515.         
  12516.         _.canvas.update_idletasks()
  12517.         _.sleep(sleep)
  12518.         items.reverse()
  12519.         for r in items:
  12520.             r.delete()
  12521.         
  12522.         _.canvas.update_idletasks()
  12523.         return EVENT_HANDLED
  12524.  
  12525.     
  12526.     def highlightPiles(_, stackinfo, sleep = 1.5):
  12527.         stackinfo = _.getHighlightPilesStacks()
  12528.         if not stackinfo:
  12529.             return 0
  12530.         
  12531.         col = _.app.opt.highlight_piles_colors
  12532.         hi = []
  12533.         for si in stackinfo:
  12534.             for s in si[0]:
  12535.                 pile = s.getPile()
  12536.             
  12537.         
  12538.         return _._highlightCards(hi, sleep)
  12539.  
  12540.     
  12541.     def shallHighlightMatch(_, stack1, card1, stack2, card2):
  12542.         return 0
  12543.  
  12544.     
  12545.     def getQuickPlayScore(_, ncards, from_stack, to_stack):
  12546.         return len(to_stack.cards) != 0
  12547.  
  12548.     
  12549.     def updateText(_):
  12550.         pass
  12551.  
  12552.     
  12553.     def getGameScore(_):
  12554.         return None
  12555.  
  12556.     
  12557.     def getGameScoreCasino(_):
  12558.         v = -len(_.cards)
  12559.         for s in _.s.foundations:
  12560.             v = v + 5 * len(s.cards)
  12561.         
  12562.         return v
  12563.  
  12564.     
  12565.     def shallUpdateBalance(_):
  12566.         if _.gstats.loaded:
  12567.             return 0
  12568.         
  12569.         if _.random.origin == _.random.ORIGIN_SELECTED:
  12570.             return 0
  12571.         
  12572.         return 1
  12573.  
  12574.     
  12575.     def getGameBalance(_):
  12576.         return 0
  12577.  
  12578.     
  12579.     def getHints(_, level, taken_hint = None):
  12580.         hint_class = _.getHintClass()
  12581.         if hint_class is None:
  12582.             return None
  12583.         
  12584.         hint = hint_class(_, level)
  12585.         return hint.getHints(taken_hint)
  12586.  
  12587.     
  12588.     def showHint(_, level = 0, sleep = 1.5, taken_hint = None):
  12589.         if _.getHintClass() is None:
  12590.             return None
  12591.         
  12592.         if level != _.hints.level:
  12593.             _.hints.level = level
  12594.             _.hints.list = None
  12595.         
  12596.         if _.hints.list is None:
  12597.             _.hints.list = _.getHints(level, taken_hint)
  12598.             _.hints.index = 0
  12599.         
  12600.         if not (_.hints.list):
  12601.             return None
  12602.         
  12603.         h = _.hints.list[_.hints.index]
  12604.         _.hints.index = _.hints.index + 1
  12605.         if _.hints.index >= len(_.hints.list):
  12606.             _.hints.index = 0
  12607.         
  12608.         (score, pos, ncards, from_stack, to_stack, text_color, forced_move) = h
  12609.         if __debug__:
  12610.             if not from_stack and len(from_stack.cards) >= ncards:
  12611.                 raise AssertionError
  12612.         if ncards == 0:
  12613.             if not __debug__ and level >= 2:
  12614.                 raise AssertionError
  12615.             if not __debug__ and from_stack is _.s.talon:
  12616.                 raise AssertionError
  12617.             return h
  12618.         elif from_stack == to_stack:
  12619.             if not __debug__ and level >= 2:
  12620.                 raise AssertionError
  12621.             if __debug__:
  12622.                 if not ncards == 1 and len(from_stack.cards) >= ncards:
  12623.                     raise AssertionError
  12624.             return h
  12625.         elif not __debug__ and to_stack:
  12626.             raise AssertionError
  12627.         if __debug__:
  12628.             if ncards <= ncards:
  12629.                 pass
  12630.             elif not ncards <= len(from_stack.cards):
  12631.                 raise AssertionError
  12632.         if not __debug__ and to_stack.acceptsCards(from_stack, from_stack.cards[-ncards:]):
  12633.             raise AssertionError
  12634.         if sleep <= 0.0:
  12635.             return h
  12636.         
  12637.         images = _.app.images
  12638.         (x1, y1) = from_stack.getPositionFor(from_stack.cards[-ncards])
  12639.         (x2, y2) = to_stack.getPositionFor(to_stack.getCard())
  12640.         (x1, y1) = (x1 + images.CARD_DX, y1 + images.CARD_DY)
  12641.         (x2, y2) = (x2 + images.CARD_DX, y2 + images.CARD_DY)
  12642.         if ncards == 1:
  12643.             x1 = x1 + images.CARDW / 2
  12644.             y1 = y1 + images.CARDH / 2
  12645.         elif from_stack.CARD_XOFFSET[0]:
  12646.             x1 = x1 + from_stack.CARD_XOFFSET[0] / 2
  12647.             y1 = y1 + images.CARDH / 2
  12648.         else:
  12649.             x1 = x1 + images.CARDW / 2
  12650.             y1 = y1 + from_stack.CARD_YOFFSET[0] / 2
  12651.         x2 = x2 + images.CARDW / 2
  12652.         y2 = y2 + images.CARDH / 2
  12653.         arrow = MfxCanvasLine(_.canvas, x1, y1, x2, y2, width = 7, fill = _.app.opt.hintarrow_color, arrow = 'last', arrowshape = (30, 30, 10))
  12654.         if level == 1 and level > 1:
  12655.             pass
  12656.         info = _.app.opt.demo_score
  12657.         if info and _.app.statusbar and _.app.opt.statusbar:
  12658.             _.app.statusbar.configLabel('info', text = 'Score %6d' % score, fg = text_color)
  12659.         else:
  12660.             info = 0
  12661.         _.canvas.update_idletasks()
  12662.         _.sleep(sleep)
  12663.         if info:
  12664.             _.app.statusbar.configLabel('info', text = '', fg = '#000000')
  12665.         
  12666.         arrow.delete()
  12667.         _.canvas.update_idletasks()
  12668.         return h
  12669.  
  12670.     
  12671.     def startDemo(_, mixed = 1, level = 2):
  12672.         if not __debug__ and level >= 2:
  12673.             raise AssertionError
  12674.         if not (_.top):
  12675.             return None
  12676.         
  12677.         _.demo = Struct(level = level, mixed = mixed, sleep = _.app.opt.demo_sleep, last_deal = [], hint = None, keypress = None, start_demo_moves = _.stats.demo_moves, info_text = None)
  12678.         _.hints.list = None
  12679.         if 1:
  12680.             _.createDemoInfoText()
  12681.         
  12682.         _.createDemoLogo()
  12683.         after_idle(_.top, _.demoEvent)
  12684.  
  12685.     
  12686.     def stopDemo(_, event = None):
  12687.         if not (_.demo):
  12688.             return None
  12689.         
  12690.         _.canvas.setTopImage(None)
  12691.         _.demo_logo = None
  12692.         _.demo = None
  12693.         _.updateMenus()
  12694.  
  12695.     
  12696.     def demoEvent(_):
  12697.         if not (_.demo) or _.demo.keypress:
  12698.             _.stopDemo()
  12699.             _.updateMenus()
  12700.             return None
  12701.         
  12702.         finished = _.playOneDemoMove(_.demo)
  12703.         _.finishMove()
  12704.         _.top.update_idletasks()
  12705.         _.hints.list = None
  12706.         player_moves = _.getPlayerMoves()
  12707.         (d, status) = (None, 0)
  12708.         bitmap = 'info'
  12709.         timeout = 10000
  12710.         if player_moves == 0:
  12711.             timeout = 5000
  12712.         
  12713.         if _.isGameWon():
  12714.             finished = 1
  12715.             if not _.top.winfo_ismapped():
  12716.                 status = 2
  12717.             elif player_moves == 0:
  12718.                 _.playSample('autopilotwon')
  12719.                 s = _.app.miscrandom.choice(('Great', 'Cool', 'Yeah', 'Wow'))
  12720.                 d = MfxDialog(_.top, title = PACKAGE + ' Autopilot', text = '\nGame solved in ' + str(_.moves.index) + ' moves.\n', image = _.app.gimages.logos[4], strings = (s,), separatorwidth = 2, timeout = timeout)
  12721.                 status = d.status
  12722.             else:
  12723.                 s = _.app.miscrandom.choice(('OK', 'OK'))
  12724.                 text = '\n   Game finished   \n'
  12725.                 if _.app.debug:
  12726.                     text = text + '\n%d %d\n' % (_.stats.player_moves, _.stats.demo_moves)
  12727.                 
  12728.                 d = MfxDialog(_.top, title = PACKAGE + ' Autopilot', text = text, bitmap = bitmap, strings = (s,), padx = 30, timeout = timeout)
  12729.                 status = d.status
  12730.         elif finished:
  12731.             if not _.top.winfo_ismapped():
  12732.                 status = 2
  12733.             elif player_moves == 0:
  12734.                 _.playSample('autopilotlost')
  12735.             
  12736.             s = _.app.miscrandom.choice(('Oh well', "That's life", 'Hmm'))
  12737.             d = MfxDialog(_.top, title = PACKAGE + ' Autopilot', text = "\nThis won't come out...\n", bitmap = bitmap, strings = (s,), padx = 30, timeout = timeout)
  12738.             status = d.status
  12739.         
  12740.         if finished:
  12741.             _.updateStats(demo = 1)
  12742.             if _.demo and status == 2 and not (_.app.debug):
  12743.                 if _.stats.demo_moves > _.demo.start_demo_moves:
  12744.                     _.app.demo_counter = _.app.demo_counter + 1
  12745.                     if _.app.demo_counter % 3 == 0:
  12746.                         if _.top.winfo_ismapped():
  12747.                             status = helpAbout(_.app, timeout = 10000)
  12748.                         
  12749.                     
  12750.                 
  12751.             
  12752.             if _.demo and status == 2:
  12753.                 demo = _.demo
  12754.                 id = _.id
  12755.                 if 1 and demo.mixed and _.app.debug:
  12756.                     gl = _.app.gdb.getGamesIdSortedById()
  12757.                     gl = _.app.gdb.getGamesIdSortedByName()
  12758.                     gl = list(gl)
  12759.                     index = (gl.index(_.id) + 1) % len(gl)
  12760.                     id = gl[index]
  12761.                 elif demo.mixed:
  12762.                     gl = _.app.gdb.getGamesIdSortedById()
  12763.                     while len(gl) > 1:
  12764.                         id = _.app.getRandomGameId()
  12765.                         if 0 or id != _.id:
  12766.                             break
  12767.                         
  12768.                 
  12769.                 if _.nextGameFlags(id) == 0:
  12770.                     _.endGame()
  12771.                     _.newGame(autoplay = 0)
  12772.                     _.startDemo(mixed = demo.mixed)
  12773.                 else:
  12774.                     _.endGame()
  12775.                     _.stopDemo()
  12776.                     _.quitGame(id, startdemo = 1)
  12777.             else:
  12778.                 _.stopDemo()
  12779.                 if 0 and _.app.debug:
  12780.                     _.endGame()
  12781.                     _.winAnimation()
  12782.                     _.newGame()
  12783.                 
  12784.         else:
  12785.             _.top.busyUpdate()
  12786.             if _.demo:
  12787.                 after_idle(_.top, _.demoEvent)
  12788.             
  12789.  
  12790.     
  12791.     def playOneDemoMove(_, demo):
  12792.         if _.moves.index > 2000:
  12793.             return 1
  12794.         
  12795.         sleep = demo.sleep
  12796.         if _.app.debug:
  12797.             if not _.top.winfo_ismapped():
  12798.                 sleep = -1.0
  12799.             
  12800.         
  12801.         if not (demo.hint) or not demo.hint[6]:
  12802.             if _._autoDeal(sound = 0):
  12803.                 return 0
  12804.             
  12805.         
  12806.         h = _.showHint(demo.level, sleep, taken_hint = demo.hint)
  12807.         demo.hint = h
  12808.         if not h:
  12809.             return 1
  12810.         
  12811.         (score, pos, ncards, from_stack, to_stack, text_color, forced_move) = h
  12812.         if ncards == 0:
  12813.             if _.dealCards() == 0:
  12814.                 return 1
  12815.             
  12816.             c = _.s.talon.getCard()
  12817.             if c in demo.last_deal:
  12818.                 return 1
  12819.             
  12820.             demo.last_deal.append(c)
  12821.         elif from_stack == to_stack:
  12822.             from_stack.flipMove()
  12823.             demo.last_deal = []
  12824.         else:
  12825.             from_stack.moveMove(ncards, to_stack, frames = -1)
  12826.             demo.last_deal = []
  12827.         return 0
  12828.  
  12829.     
  12830.     def createDemoInfoText(_):
  12831.         return None
  12832.         if not (_.demo) and _.demo.info_text or _.preview:
  12833.             return None
  12834.         
  12835.         tinfo = [
  12836.             ('sw', 8, _.height - 8),
  12837.             ('se', _.width - 8, _.height - 8),
  12838.             ('nw', 8, 8),
  12839.             ('ne', _.width - 8, 8)]
  12840.         ta = _.getDemoInfoTextAttr(tinfo)
  12841.         if ta:
  12842.             font = getFont('canvas_large')
  12843.             _.demo.info_text = MfxCanvasText(_.canvas, ta[1], ta[2], anchor = ta[0], font = font, text = _.getDemoInfoText())
  12844.         
  12845.  
  12846.     
  12847.     def getDemoInfoText(_):
  12848.         return _.gameinfo.short_name
  12849.  
  12850.     
  12851.     def getDemoInfoTextAttr(_, tinfo):
  12852.         (items1, items2) = ([], [])
  12853.         for s in _.allstacks:
  12854.             if s.is_visible:
  12855.                 items1.append(s)
  12856.                 items1.extend(list(s.cards))
  12857.                 if not (s.cards) and s.cap.max_accept > 0:
  12858.                     items2.append(s)
  12859.                 else:
  12860.                     items2.extend(list(s.cards))
  12861.             
  12862.         
  12863.         ti = _._Game__checkFreeSpaceForDemoInfoText(items1)
  12864.         if ti < 0:
  12865.             ti = _._Game__checkFreeSpaceForDemoInfoText(items2)
  12866.         
  12867.         if ti < 0:
  12868.             return None
  12869.         
  12870.         return tinfo[ti]
  12871.  
  12872.     
  12873.     def __checkFreeSpaceForDemoInfoText(_, items):
  12874.         (CW, CH) = (_.app.images.CARDW, _.app.images.CARDH)
  12875.         (x1, x2) = (3 * CW / 2, _.width - 5 * CW / 2)
  12876.         (y1, y2) = (CH / 2, _.height - 3 * CH / 2)
  12877.         m = [
  12878.             1,
  12879.             1,
  12880.             1,
  12881.             1]
  12882.         for c in items:
  12883.             (cx, cy) = (c.x, c.y)
  12884.             if cy >= y2:
  12885.                 if cx <= x1:
  12886.                     m[0] = 0
  12887.                 elif cx >= x2:
  12888.                     m[1] = 0
  12889.                 
  12890.             elif cy <= y1:
  12891.                 if cx <= x1:
  12892.                     m[2] = 0
  12893.                 elif cx >= x2:
  12894.                     m[3] = 0
  12895.                 
  12896.             
  12897.         
  12898.         for mm in m:
  12899.             pass
  12900.         
  12901.         return -1
  12902.  
  12903.     
  12904.     def createDemoLogo(_):
  12905.         if not (_.app.gimages.demo):
  12906.             return None
  12907.         
  12908.         if _.demo_logo or not (_.app.opt.demo_logo):
  12909.             return None
  12910.         
  12911.         if _.width <= 100 or _.height <= 100:
  12912.             return None
  12913.         
  12914.         n = _.random.initial_seed % len(_.app.gimages.demo)
  12915.         _.demo_logo = _.app.gimages.demo[int(n)]
  12916.         _.canvas.setTopImage(_.demo_logo, cw = _.width, ch = _.height)
  12917.  
  12918.     
  12919.     def startMoves(_):
  12920.         _.moves = Struct(state = _.S_PLAY, history = [], index = 0, current = [])
  12921.         _.stats.undo_moves = 0
  12922.         _.stats.redo_moves = 0
  12923.         _.stats.player_moves = 0
  12924.         _.stats.demo_moves = 0
  12925.         _.stats.total_moves = 0
  12926.         _.stats.quickplay_moves = 0
  12927.         _.stats.goto_bookmark_moves = 0
  12928.  
  12929.     
  12930.     def __storeMove(_, am):
  12931.         if _.moves.state <= _.moves.state:
  12932.             pass
  12933.         elif _.moves.state <= _.S_PLAY:
  12934.             _.moves.current.append(am)
  12935.         
  12936.  
  12937.     
  12938.     def moveMove(_, ncards, from_stack, to_stack, frames = -1, shadow = -1):
  12939.         if __debug__:
  12940.             if not from_stack and to_stack and from_stack is not to_stack:
  12941.                 raise AssertionError
  12942.         if __debug__:
  12943.             if ncards < ncards:
  12944.                 pass
  12945.             elif not ncards <= len(from_stack.cards):
  12946.                 raise AssertionError
  12947.         am = AMoveMove(ncards, from_stack, to_stack, frames, shadow)
  12948.         _._Game__storeMove(am)
  12949.         am.do(_)
  12950.         _.hints.list = None
  12951.  
  12952.     
  12953.     def flipMove(_, stack):
  12954.         if not __debug__ and stack:
  12955.             raise AssertionError
  12956.         am = AFlipMove(stack)
  12957.         _._Game__storeMove(am)
  12958.         am.do(_)
  12959.         _.hints.list = None
  12960.  
  12961.     
  12962.     def turnStackMove(_, from_stack, to_stack, update_flags = 1):
  12963.         if __debug__:
  12964.             if not from_stack and to_stack and from_stack is not to_stack:
  12965.                 raise AssertionError
  12966.         if not __debug__ and len(to_stack.cards) == 0:
  12967.             raise AssertionError
  12968.         am = ATurnStackMove(from_stack, to_stack, update_flags = update_flags)
  12969.         _._Game__storeMove(am)
  12970.         am.do(_)
  12971.         _.hints.list = None
  12972.  
  12973.     
  12974.     def nextRoundMove(_, stack):
  12975.         if not __debug__ and stack:
  12976.             raise AssertionError
  12977.         am = ANextRoundMove(stack)
  12978.         _._Game__storeMove(am)
  12979.         am.do(_)
  12980.         _.hints.list = None
  12981.  
  12982.     
  12983.     def saveSeedMove(_):
  12984.         am = ASaveSeedMove(_)
  12985.         _._Game__storeMove(am)
  12986.         am.do(_)
  12987.  
  12988.     
  12989.     def shuffleStackMove(_, stack):
  12990.         if not __debug__ and stack:
  12991.             raise AssertionError
  12992.         am = AShuffleStackMove(stack, _)
  12993.         _._Game__storeMove(am)
  12994.         am.do(_)
  12995.         _.hints.list = None
  12996.  
  12997.     
  12998.     def updateStackMove(_, stack, flags):
  12999.         if not __debug__ and stack:
  13000.             raise AssertionError
  13001.         am = AUpdateStackMove(stack, flags)
  13002.         _._Game__storeMove(am)
  13003.         am.do(_)
  13004.  
  13005.     
  13006.     def finishMove(_):
  13007.         (current, moves, stats) = (_.moves.current, _.moves, _.stats)
  13008.         if not current:
  13009.             return 0
  13010.         
  13011.         _.hints.list = None
  13012.         if _.demo:
  13013.             stats.demo_moves = stats.demo_moves + 1
  13014.             if moves.index == 0:
  13015.                 stats.player_moves = 0
  13016.             
  13017.         else:
  13018.             stats.player_moves = stats.player_moves + 1
  13019.             if moves.index == 0:
  13020.                 stats.demo_moves = 0
  13021.             
  13022.         stats.total_moves = stats.total_moves + 1
  13023.         redo = 0
  13024.         if moves.index + 1 < len(moves.history):
  13025.             (l, m) = (len(current), moves.history[moves.index])
  13026.             if l == len(m):
  13027.                 for i in range(l):
  13028.                     a1 = current[i]
  13029.                     a2 = m[i]
  13030.                 else:
  13031.                     redo = 1
  13032.             
  13033.         
  13034.         if redo:
  13035.             moves.history[moves.index] = current
  13036.             moves.index = moves.index + 1
  13037.         else:
  13038.             moves.history[moves.index:] = [
  13039.                 current]
  13040.             moves.index = moves.index + 1
  13041.             if not __debug__ and moves.index == len(moves.history):
  13042.                 raise AssertionError
  13043.         moves.current = []
  13044.         _.updateText()
  13045.         _.updateStatus(moves = moves.index)
  13046.         _.updateMenus()
  13047.         return 1
  13048.  
  13049.     
  13050.     def undo(_):
  13051.         if not __debug__ and _.canUndo():
  13052.             raise AssertionError
  13053.         if __debug__:
  13054.             if not _.moves.state == _.S_PLAY and _.moves.current == []:
  13055.                 raise AssertionError
  13056.         if __debug__:
  13057.             if _.moves.index <= _.moves.index:
  13058.                 pass
  13059.             elif not _.moves.index <= len(_.moves.history):
  13060.                 raise AssertionError
  13061.         if _.moves.index == 0:
  13062.             return None
  13063.         
  13064.         _.moves.index = _.moves.index - 1
  13065.         m = _.moves.history[_.moves.index]
  13066.         m = m[:]
  13067.         m.reverse()
  13068.         _.moves.state = _.S_UNDO
  13069.         for atomic_move in m:
  13070.             atomic_move.undo(_)
  13071.         
  13072.         _.moves.state = _.S_PLAY
  13073.         _.stats.undo_moves = _.stats.undo_moves + 1
  13074.         _.stats.total_moves = _.stats.total_moves + 1
  13075.         _.hints.list = None
  13076.         _.updateText()
  13077.         _.updateStatus(moves = _.moves.index)
  13078.         _.updateMenus()
  13079.  
  13080.     
  13081.     def redo(_):
  13082.         if not __debug__ and _.canRedo():
  13083.             raise AssertionError
  13084.         if __debug__:
  13085.             if not _.moves.state == _.S_PLAY and _.moves.current == []:
  13086.                 raise AssertionError
  13087.         if __debug__:
  13088.             if _.moves.index <= _.moves.index:
  13089.                 pass
  13090.             elif not _.moves.index <= len(_.moves.history):
  13091.                 raise AssertionError
  13092.         if _.moves.index == len(_.moves.history):
  13093.             return None
  13094.         
  13095.         m = _.moves.history[_.moves.index]
  13096.         _.moves.index = _.moves.index + 1
  13097.         _.moves.state = _.S_REDO
  13098.         for atomic_move in m:
  13099.             atomic_move.redo(_)
  13100.         
  13101.         _.moves.state = _.S_PLAY
  13102.         _.stats.redo_moves = _.stats.redo_moves + 1
  13103.         _.stats.total_moves = _.stats.total_moves + 1
  13104.         _.hints.list = None
  13105.         _.updateText()
  13106.         _.updateStatus(moves = _.moves.index)
  13107.         _.updateMenus()
  13108.  
  13109.     
  13110.     def setBookmark(_, n, confirm = 1):
  13111.         _.finishMove()
  13112.         if not _.canSetBookmark():
  13113.             return 0
  13114.         
  13115.         if confirm < 0:
  13116.             confirm = _.app.opt.confirm
  13117.         
  13118.         if confirm and _.gsaveinfo.bookmarks.get(n):
  13119.             if not _.areYouSure('Set bookmark', 'Replace existing bookmark %d ?' % (n + 1)):
  13120.                 return 0
  13121.             
  13122.         
  13123.         file = StringIO.StringIO()
  13124.         p = Pickler(file, 1)
  13125.         
  13126.         try:
  13127.             _._dumpGame(p, bookmark = 2)
  13128.             bm = (file.getvalue(), _.moves.index)
  13129.         except:
  13130.             pass
  13131.  
  13132.         _.gsaveinfo.bookmarks[n] = bm
  13133.         return 1
  13134.         return 0
  13135.  
  13136.     
  13137.     def gotoBookmark(_, n, confirm = -1, update_stats = 1):
  13138.         _.finishMove()
  13139.         bm = _.gsaveinfo.bookmarks.get(n)
  13140.         if not bm:
  13141.             return None
  13142.         
  13143.         if confirm < 0:
  13144.             confirm = _.app.opt.confirm
  13145.         
  13146.         if confirm:
  13147.             if not _.areYouSure('Goto bookmark', 'Goto bookmark %d ?' % (n + 1)):
  13148.                 return None
  13149.             
  13150.         
  13151.         
  13152.         try:
  13153.             (s, moves_index) = bm
  13154.             _.setCursor(cursor = CURSOR_WATCH)
  13155.             file = StringIO.StringIO(s)
  13156.             p = Unpickler(file)
  13157.             game = _._undumpGame(p, _.app)
  13158.             if not __debug__ and game.id == _.id:
  13159.                 raise AssertionError
  13160.             _.setBookmark(-1, confirm = 0)
  13161.         except:
  13162.             del _.gsaveinfo.bookmarks[n]
  13163.             _.setCursor(cursor = _.app.top_cursor)
  13164.  
  13165.         if update_stats:
  13166.             _.stats.goto_bookmark_moves = _.stats.goto_bookmark_moves + 1
  13167.             _.gstats.goto_bookmark_moves = _.gstats.goto_bookmark_moves + 1
  13168.         
  13169.         _.restoreGame(game, reset = 0)
  13170.         destruct(game)
  13171.  
  13172.     
  13173.     def undoGotoBookmark(_):
  13174.         _.gotoBookmark(-1, update_stats = 0)
  13175.  
  13176.     
  13177.     def loadGame(_, filename):
  13178.         if _.changed():
  13179.             if not _.areYouSure('Open game'):
  13180.                 return None
  13181.             
  13182.         
  13183.         _.finishMove()
  13184.         game = None
  13185.         _.setCursor(cursor = CURSOR_WATCH)
  13186.         _.disableMenus()
  13187.         
  13188.         try:
  13189.             game = _._loadGame(filename, _.app)
  13190.             game.gstats.holded = 0
  13191.         except AssertionError:
  13192.             ex = None
  13193.             _.updateMenus()
  13194.             _.setCursor(cursor = _.app.top_cursor)
  13195.             d = MfxDialog(_.top, title = 'Load game error', bitmap = 'error', text = 'Error while loading game.\n\nProbably the game file is damaged,\nbut this could also be a bug you might want to report.')
  13196.         except (Exception, UnpicklingError):
  13197.             ex = None
  13198.             _.updateMenus()
  13199.             _.setCursor(cursor = _.app.top_cursor)
  13200.             d = MfxExceptionDialog(_.top, ex, title = 'Load game error', text = 'Error while loading game')
  13201.         except:
  13202.             _.updateMenus()
  13203.             _.setCursor(cursor = _.app.top_cursor)
  13204.             d = MfxDialog(_.top, title = 'Load game error', bitmap = 'error', text = 'Internal error while loading game.\n\nPlease report this bug.')
  13205.  
  13206.         _.filename = filename
  13207.         game.filename = filename
  13208.         if _.nextGameFlags(game.id) == 0:
  13209.             _.endGame()
  13210.             _.restoreGame(game)
  13211.             destruct(game)
  13212.         else:
  13213.             _.endGame()
  13214.             _.quitGame(game.id, loadedgame = game)
  13215.  
  13216.     
  13217.     def saveGame(_, filename, binmode = 1):
  13218.         _.finishMove()
  13219.         _.setCursor(cursor = CURSOR_WATCH)
  13220.         
  13221.         try:
  13222.             _._saveGame(filename, binmode)
  13223.         except Exception:
  13224.             ex = None
  13225.             _.setCursor(cursor = _.app.top_cursor)
  13226.             d = MfxExceptionDialog(_.top, ex, title = 'Save game error', text = 'Error while saving game')
  13227.  
  13228.         _.filename = filename
  13229.         _.setCursor(cursor = _.app.top_cursor)
  13230.  
  13231.     
  13232.     def _loadGame(_, filename, app):
  13233.         game = None
  13234.         f = None
  13235.         
  13236.         try:
  13237.             f = open(filename, 'rb')
  13238.             p = Unpickler(f)
  13239.             game = _._undumpGame(p, app)
  13240.             game.gstats.loaded = game.gstats.loaded + 1
  13241.         finally:
  13242.             if f:
  13243.                 f.close()
  13244.             
  13245.  
  13246.         return game
  13247.  
  13248.     
  13249.     def _getUndumpVersion(_, version_tuple):
  13250.         if version_tuple > (4, 41):
  13251.             return 4
  13252.         
  13253.         if version_tuple > (4, 30):
  13254.             return 3
  13255.         
  13256.         if version_tuple > (4, 20):
  13257.             return 2
  13258.         
  13259.         if version_tuple > (3, 20):
  13260.             return 1
  13261.         
  13262.         if version_tuple >= (2, 99):
  13263.             return 0
  13264.         
  13265.         return -1
  13266.  
  13267.     
  13268.     def _undumpGame(_, p, app):
  13269.         _.updateTime()
  13270.         
  13271.         def check(expr, text = None):
  13272.             if not expr:
  13273.                 if not text:
  13274.                     text = 'Invalid or damaged ' + PACKAGE + ' save file'
  13275.                 
  13276.                 raise Exception, str(text)
  13277.             
  13278.  
  13279.         
  13280.         def pload(t = None, p = p):
  13281.             obj = p.load()
  13282.             if type(t) is types.TypeType:
  13283.                 if type(obj) is not t:
  13284.                     text = 'Invalid or damaged ' + PACKAGE + ' save file'
  13285.                     raise Exception, text
  13286.                 
  13287.             
  13288.             return obj
  13289.  
  13290.         package = pload()
  13291.         if type(package) is types.StringType:
  13292.             pass
  13293.         check(package == PACKAGE)
  13294.         version = pload()
  13295.         if type(version) is types.StringType:
  13296.             pass
  13297.         check(len(version) <= 20)
  13298.         version_tuple = get_version_tuple(version)
  13299.         v = _._getUndumpVersion(version_tuple)
  13300.         if v >= 0:
  13301.             pass
  13302.         check(version_tuple <= VERSION_TUPLE, 'Cannot load games saved with\n' + PACKAGE + ' version ' + version)
  13303.         game_version = 1
  13304.         bookmark = 0
  13305.         if v >= 2:
  13306.             vt = pload()
  13307.             if type(vt) is types.TupleType:
  13308.                 pass
  13309.             check(vt == version_tuple)
  13310.             bookmark = pload()
  13311.             if type(bookmark) is types.IntType:
  13312.                 pass
  13313.             None(check if bookmark <= bookmark else bookmark <= 2, 'Incompatible savegame format')
  13314.             game_version = pload()
  13315.             if type(game_version) is types.IntType:
  13316.                 pass
  13317.             check(game_version > 0)
  13318.             if v <= 3:
  13319.                 bookmark = 0
  13320.             
  13321.         
  13322.         id = pload()
  13323.         if type(id) is types.IntType:
  13324.             pass
  13325.         check(id > 0)
  13326.         if not GI.PROTECTED_GAMES.has_key(id):
  13327.             game = app.constructGame(id)
  13328.             if game:
  13329.                 if not game.canLoadGame(version_tuple, game_version):
  13330.                     destruct(game)
  13331.                     game = None
  13332.                 
  13333.             
  13334.         
  13335.         check(game is not None, 'Cannot load this game from version ' + version + '\nas the game rules have changed\nin the current implementation.')
  13336.         game.version = version
  13337.         game.version_tuple = version_tuple
  13338.         game.random = pload()
  13339.         if type(game.random) is types.TupleType:
  13340.             game.random = WHRandom(game.random)
  13341.         
  13342.         check(type(game.random) is types.InstanceType)
  13343.         check(isinstance(game.random, PysolRandom))
  13344.         if not hasattr(game.random, 'origin'):
  13345.             game.random.origin = game.random.ORIGIN_UNKNOWN
  13346.         
  13347.         game.loadinfo.stacks = []
  13348.         game.loadinfo.ncards = 0
  13349.         nstacks = pload()
  13350.         if type(nstacks) is types.IntType:
  13351.             pass
  13352.         None(check if nstacks <= nstacks else nstacks <= 255)
  13353.         for i in range(nstacks):
  13354.             stack = []
  13355.             ncards = pload()
  13356.             if type(ncards) is types.IntType:
  13357.                 pass
  13358.             0(check if ncards <= ncards else ncards <= 1024)
  13359.             for j in range(ncards):
  13360.                 card_id = pload(types.IntType)
  13361.                 face_up = pload(types.IntType)
  13362.                 stack.append((card_id, face_up))
  13363.             
  13364.             game.loadinfo.stacks.append(stack)
  13365.             game.loadinfo.ncards = game.loadinfo.ncards + ncards
  13366.         
  13367.         check(game.loadinfo.ncards == game.gameinfo.ncards)
  13368.         game.loadinfo.talon_round = pload()
  13369.         if bookmark <= bookmark:
  13370.             pass
  13371.         elif bookmark <= 1:
  13372.             if v >= 3:
  13373.                 saveinfo = pload()
  13374.                 check(isinstance(saveinfo, Struct))
  13375.                 merge_dict(game.saveinfo.__dict__, saveinfo.__dict__)
  13376.                 if v >= 4:
  13377.                     gsaveinfo = pload()
  13378.                     check(isinstance(gsaveinfo, Struct))
  13379.                     merge_dict(game.gsaveinfo.__dict__, gsaveinfo.__dict__)
  13380.                 
  13381.             elif v >= 1:
  13382.                 talon_base_cards = pload(types.ListType)
  13383.             
  13384.         
  13385.         moves = pload()
  13386.         check(isinstance(moves, Struct))
  13387.         merge_dict(game.moves.__dict__, moves.__dict__)
  13388.         if bookmark <= bookmark:
  13389.             pass
  13390.         elif bookmark <= 1:
  13391.             gstats = pload()
  13392.             check(isinstance(gstats, Struct))
  13393.             merge_dict(game.gstats.__dict__, gstats.__dict__)
  13394.             stats = pload()
  13395.             check(isinstance(stats, Struct))
  13396.             merge_dict(game.stats.__dict__, stats.__dict__)
  13397.         
  13398.         game._loadGameHook(p)
  13399.         if v >= 4:
  13400.             dummy = pload(types.StringType)
  13401.             check(dummy == 'EOF')
  13402.         
  13403.         if bookmark == 2:
  13404.             game.stats = _.stats
  13405.             game.gstats = _.gstats
  13406.             game.saveinfo = _.saveinfo
  13407.             game.gsaveinfo = _.gsaveinfo
  13408.         
  13409.         return game
  13410.  
  13411.     
  13412.     def _saveGame(_, filename, binmode = 1):
  13413.         f = None
  13414.         
  13415.         try:
  13416.             if not _.canSaveGame():
  13417.                 raise Exception, 'Cannot save this game.'
  13418.             
  13419.             f = open(filename, 'wb')
  13420.             p = Pickler(f, binmode)
  13421.             _._dumpGame(p)
  13422.         finally:
  13423.             if f:
  13424.                 f.close()
  13425.             
  13426.  
  13427.  
  13428.     
  13429.     def _dumpGame(_, p, bookmark = 0):
  13430.         _.updateTime()
  13431.         if __debug__:
  13432.             if bookmark <= bookmark:
  13433.                 pass
  13434.             elif not bookmark <= 2:
  13435.                 raise AssertionError
  13436.         p.dump(PACKAGE)
  13437.         p.dump(VERSION)
  13438.         p.dump(VERSION_TUPLE)
  13439.         p.dump(bookmark)
  13440.         p.dump(_.GAME_VERSION)
  13441.         p.dump(_.id)
  13442.         p.dump(_.random)
  13443.         p.dump(len(_.allstacks))
  13444.         for stack in _.allstacks:
  13445.             p.dump(len(stack.cards))
  13446.             for card in stack.cards:
  13447.                 p.dump(card.id)
  13448.                 p.dump(card.face_up)
  13449.             
  13450.         
  13451.         p.dump(_.s.talon.round)
  13452.         if bookmark <= bookmark:
  13453.             pass
  13454.         elif bookmark <= 1:
  13455.             p.dump(_.saveinfo)
  13456.             p.dump(_.gsaveinfo)
  13457.         
  13458.         p.dump(_.moves)
  13459.         if bookmark <= bookmark:
  13460.             pass
  13461.         elif bookmark <= 1:
  13462.             p.dump(_.gstats)
  13463.             p.dump(_.stats)
  13464.         
  13465.         _._saveGameHook(p)
  13466.         p.dump('EOF')
  13467.  
  13468.     
  13469.     def _restoreGameHook(_, game):
  13470.         pass
  13471.  
  13472.     
  13473.     def _loadGameHook(_, p):
  13474.         pass
  13475.  
  13476.     
  13477.     def _saveGameHook(_, p):
  13478.         pass
  13479.  
  13480.  
  13481.  
  13482. class _LayoutStack:
  13483.     
  13484.     def __init__(_, x, y, suit = None):
  13485.         _.x = int(round(x))
  13486.         _.y = int(round(y))
  13487.         _.suit = suit
  13488.         _.text_args = { }
  13489.         _.text_format = '%d'
  13490.  
  13491.     
  13492.     def setText(_, x, y, anchor = 'center', format = None, **kw):
  13493.         _.text_args['x'] = x
  13494.         _.text_args['y'] = y
  13495.         _.text_args['anchor'] = anchor
  13496.         _.text_args.update(kw)
  13497.         if format is not None:
  13498.             _.text_format = format
  13499.         
  13500.  
  13501.  
  13502.  
  13503. class Layout:
  13504.     
  13505.     def __init__(_, game, XM = 10, YM = 10, **kw):
  13506.         _.game = game
  13507.         _.canvas = _.game.canvas
  13508.         _.size = None
  13509.         _.s = Struct(talon = None, waste = None, foundations = [], rows = [], reserves = [])
  13510.         _.stackmap = { }
  13511.         _.regions = []
  13512.         images = _.game.app.images
  13513.         _.CW = images.CARDW
  13514.         _.CH = images.CARDH
  13515.         _.XM = XM
  13516.         _.YM = YM
  13517.         _.XS = _.CW + XM
  13518.         _.YS = _.CH + YM
  13519.         _.XOFFSET = images.CARD_XOFFSET
  13520.         _.YOFFSET = images.CARD_YOFFSET
  13521.         _.__dict__.update(kw)
  13522.         if _.game.preview > 1:
  13523.             if kw.has_key('XOFFSET'):
  13524.                 _.XOFFSET = _.XOFFSET / _.game.preview
  13525.             
  13526.             if kw.has_key('YOFFSET'):
  13527.                 _.YOFFSET = _.YOFFSET / _.game.preview
  13528.             
  13529.         
  13530.  
  13531.     
  13532.     def __createStack(_, x, y, suit = None):
  13533.         stack = _LayoutStack(x, y, suit)
  13534.         mapkey = (stack.x, stack.y)
  13535.         if not __debug__ and not _.stackmap.has_key(mapkey):
  13536.             raise AssertionError
  13537.         _.stackmap[mapkey] = stack
  13538.         return stack
  13539.  
  13540.     
  13541.     def getTextAttr(_, stack, anchor):
  13542.         (x, y) = (0, 0)
  13543.         if stack is not None:
  13544.             (x, y) = (stack.x, stack.y)
  13545.         
  13546.         if anchor == 'n':
  13547.             return (x + _.CW / 2, y - _.YM, 'center', '%d')
  13548.         
  13549.         if anchor == 'nn':
  13550.             return (x + _.CW / 2, y - _.YM, 's', '%d')
  13551.         
  13552.         if anchor == 's':
  13553.             return (x + _.CW / 2, y + _.YS, 'center', '%d')
  13554.         
  13555.         if anchor == 'ss':
  13556.             return (x + _.CW / 2, y + _.YS, 'n', '%d')
  13557.         
  13558.         if anchor == 'nw':
  13559.             return (x - _.XM, y, 'ne', '%d')
  13560.         
  13561.         if anchor == 'sw':
  13562.             return (x - _.XM, y + _.CH, 'se', '%d')
  13563.         
  13564.         f = '%2d'
  13565.         if _.game.gameinfo.decks > 1:
  13566.             f = '%3d'
  13567.         
  13568.         if anchor == 'ne':
  13569.             return (x + _.XS, y, 'nw', f)
  13570.         
  13571.         if anchor == 'se':
  13572.             return (x + _.XS, y + _.CH, 'sw', f)
  13573.         
  13574.         if anchor == 'e':
  13575.             return (x + _.XS, y + _.CH / 2, 'w', f)
  13576.         
  13577.         raise Exception, anchor
  13578.  
  13579.     
  13580.     def createText(_, stack, anchor, dx = 0, dy = 0, text_format = ''):
  13581.         if _.canvas.preview > 1:
  13582.             return None
  13583.         
  13584.         if not __debug__ and stack.texts.ncards is None:
  13585.             raise AssertionError
  13586.         (tx, ty, ta, tf) = _.getTextAttr(stack, anchor)
  13587.         stack.texts.ncards = MfxCanvasText(_.canvas, tx + dx, ty + dy, anchor = ta)
  13588.         if not text_format:
  13589.             pass
  13590.         stack.texts.ncards.text_format = tf
  13591.  
  13592.     
  13593.     def setRegion(_, stacks, rects):
  13594.         _.regions.append((stacks, rects))
  13595.  
  13596.     
  13597.     def defaultAll(_):
  13598.         game = _.game
  13599.         if game.s.talon:
  13600.             game.s.talon.texts.ncards = _.defaultText(_.s.talon)
  13601.         
  13602.         if game.s.waste:
  13603.             game.s.waste.texts.ncards = _.defaultText(_.s.waste)
  13604.         
  13605.         _.defaultStackGroups()
  13606.         _.defaultRegions()
  13607.  
  13608.     
  13609.     def defaultText(_, layout_stack):
  13610.         if _.canvas.preview > 1:
  13611.             return None
  13612.         
  13613.         if layout_stack is None or not (layout_stack.text_args):
  13614.             return None
  13615.         
  13616.         t = apply(MfxCanvasText, (_.game.canvas,), layout_stack.text_args)
  13617.         t.text_format = layout_stack.text_format
  13618.         return t
  13619.  
  13620.     
  13621.     def defaultStackGroups(_):
  13622.         game = _.game
  13623.         waste = []
  13624.         if game.s.waste is not None:
  13625.             waste = [
  13626.                 game.s.waste]
  13627.         
  13628.         game.sg.talonstacks = [
  13629.             game.s.talon] + waste
  13630.         game.sg.dropstacks = game.s.rows + game.s.reserves + waste
  13631.         game.sg.openstacks = game.s.foundations + game.s.rows + game.s.reserves
  13632.         game.sg.reservestacks = game.s.reserves
  13633.  
  13634.     
  13635.     def defaultRegions(_):
  13636.         for region in _.regions:
  13637.             stacks = []
  13638.             for s in region[0]:
  13639.                 mapkey = (s.x, s.y)
  13640.                 id = _.game.stackmap[mapkey]
  13641.                 stacks.append(_.game.allstacks[id])
  13642.             
  13643.             _.game.setRegion(stacks, region[1])
  13644.         
  13645.  
  13646.     
  13647.     def bakersDozenLayout(_, rows, texts = 0, playcards = 9):
  13648.         S = _._Layout__createStack
  13649.         (CW, CH) = (_.CW, _.CH)
  13650.         (XM, YM) = (_.XM, _.YM)
  13651.         (XS, YS) = (_.XS, _.YS)
  13652.         decks = _.game.gameinfo.decks
  13653.         suits = len(_.game.gameinfo.suits) + bool(_.game.gameinfo.trumps)
  13654.         halfrows = (rows + 1) / 2
  13655.         h = YS + min(2 * YS, (playcards - 1) * _.YOFFSET)
  13656.         h = max(h, 5 * YS / 2, 3 * YS / 2 + CH)
  13657.         h = min(h, 3 * YS)
  13658.         (x, y) = (XM, YM)
  13659.         for i in range(halfrows):
  13660.             _.s.rows.append(S(x + i * XS, y))
  13661.         
  13662.         for i in range(rows - halfrows):
  13663.             _.s.rows.append(S(x + i * XS, y + h))
  13664.         
  13665.         (x, y) = (XM + halfrows * XS, YM)
  13666.         _.setRegion(_.s.rows, (-999, -999, x - CW / 2, 999999))
  13667.         for suit in range(suits):
  13668.             for i in range(decks):
  13669.                 _.s.foundations.append(S(x + i * XS, y, suit = suit))
  13670.             
  13671.             y = y + YS
  13672.         
  13673.         h = YM + 2 * h
  13674.         _.size = (XM + (halfrows + decks) * XS, h)
  13675.  
  13676.     
  13677.     def freeCellLayout(_, rows, reserves, texts = 0, playcards = 18):
  13678.         S = _._Layout__createStack
  13679.         (CW, CH) = (_.CW, _.CH)
  13680.         (XM, YM) = (_.XM, _.YM)
  13681.         (XS, YS) = (_.XS, _.YS)
  13682.         decks = _.game.gameinfo.decks
  13683.         suits = len(_.game.gameinfo.suits) + bool(_.game.gameinfo.trumps)
  13684.         toprows = reserves + 1 + suits * decks
  13685.         maxrows = max(rows, toprows)
  13686.         w = XM + maxrows * XS
  13687.         h = CH * 2 / 3 + (playcards - 1) * _.YOFFSET
  13688.         h = YM + YS + max(h, 3 * YS)
  13689.         (x, y) = ((w - toprows * XS - XM) / 2, YM)
  13690.         for i in range(reserves):
  13691.             _.s.reserves.append(S(x, y))
  13692.             x = x + XS
  13693.         
  13694.         for suit in range(suits):
  13695.             for i in range(decks):
  13696.                 x = x + XS
  13697.                 _.s.foundations.append(S(x, y, suit = suit))
  13698.             
  13699.         
  13700.         (x, y) = ((w - rows * XS - XM) / 2, YM + YS)
  13701.         for i in range(rows):
  13702.             _.s.rows.append(S(x, y))
  13703.             x = x + XS
  13704.         
  13705.         _.setRegion(_.s.rows, (-999, y - YM / 2, 999999, 999999))
  13706.         (x, y) = (XM, h - YS)
  13707.         _.size = (XM + (rows + decks) * XS, h)
  13708.  
  13709.     
  13710.     def gypsyLayout(_, rows, waste = 0, texts = 1, playcards = 25):
  13711.         S = _._Layout__createStack
  13712.         (CW, CH) = (_.CW, _.CH)
  13713.         (XM, YM) = (_.XM, _.YM)
  13714.         (XS, YS) = (_.XS, _.YS)
  13715.         decks = _.game.gameinfo.decks
  13716.         suits = len(_.game.gameinfo.suits) + bool(_.game.gameinfo.trumps)
  13717.         h = CH * 2 / 3 + (playcards - 1) * _.YOFFSET
  13718.         h = YM + max(h, (suits + 1) * YS)
  13719.         (x, y) = (XM, YM)
  13720.         for i in range(rows):
  13721.             _.s.rows.append(S(x, y))
  13722.             x = x + XS
  13723.         
  13724.         _.setRegion(_.s.rows, (-999, -999, x - CW / 2, 999999))
  13725.         for suit in range(suits):
  13726.             for i in range(decks):
  13727.                 _.s.foundations.append(S(x + i * XS, y, suit = suit))
  13728.             
  13729.             y = y + YS
  13730.         
  13731.         (x, y) = (x + (decks - 1) * XS, h - YS)
  13732.         _.size = (XM + (rows + decks) * XS, h)
  13733.  
  13734.     
  13735.     def harpLayout(_, rows, waste, texts = 1, playcards = 19):
  13736.         S = _._Layout__createStack
  13737.         (CW, CH) = (_.CW, _.CH)
  13738.         (XM, YM) = (_.XM, _.YM)
  13739.         (XS, YS) = (_.XS, _.YS)
  13740.         decks = _.game.gameinfo.decks
  13741.         suits = len(_.game.gameinfo.suits) + bool(_.game.gameinfo.trumps)
  13742.         w = max(rows * XS, (suits * decks + waste + 1) * XS, (suits * decks + 1) * XS + 2 * XM)
  13743.         w = XM + w
  13744.         h = YS + (playcards - 1) * _.YOFFSET
  13745.         h = max(h, 3 * YS)
  13746.         (x, y) = ((w - rows * XS - XM) / 2, YM)
  13747.         for i in range(rows):
  13748.             _.s.rows.append(S(x, y))
  13749.             x = x + XS
  13750.         
  13751.         (x, y) = (XM, YM + h)
  13752.         _.setRegion(_.s.rows, (-999, -999, 999999, y - YS / 2))
  13753.         for suit in range(suits):
  13754.             for i in range(decks):
  13755.                 _.s.foundations.append(S(x, y, suit = suit))
  13756.                 x = x + XS
  13757.             
  13758.         
  13759.         x = w - XS
  13760.         _.size = (w, YM + h + YS)
  13761.  
  13762.     
  13763.     def klondikeLayout(_, rows, waste, texts = 1, playcards = 16, center = 1):
  13764.         S = _._Layout__createStack
  13765.         (CW, CH) = (_.CW, _.CH)
  13766.         (XM, YM) = (_.XM, _.YM)
  13767.         (XS, YS) = (_.XS, _.YS)
  13768.         decks = _.game.gameinfo.decks
  13769.         suits = len(_.game.gameinfo.suits) + bool(_.game.gameinfo.trumps)
  13770.         frows = suits * decks
  13771.         toprows = 1 + waste + frows
  13772.         maxrows = max(rows, toprows)
  13773.         yextra = 0
  13774.         h = CH * 2 / 3 + (playcards - 1) * _.YOFFSET
  13775.         h = max(h, 2 * YS)
  13776.         (x, y) = (XM, YM)
  13777.         _.s.talon = s = S(x, y)
  13778.         if texts:
  13779.             if waste and not center or maxrows - frows <= 1:
  13780.                 s.setText(x + CW / 2, y + YS, anchor = 'n')
  13781.                 yextra = 20
  13782.             else:
  13783.                 s.setText(x + XS, y, anchor = 'nw', format = '%3d')
  13784.         
  13785.         if waste:
  13786.             x = x + XS
  13787.             _.s.waste = s = S(x, y)
  13788.             if texts:
  13789.                 s.setText(x + CW / 2, y + YS, anchor = 'n')
  13790.             
  13791.         
  13792.         x = XM + (maxrows - frows) * XS
  13793.         if center and frows + 2 * (1 + waste + 1) <= maxrows:
  13794.             x = XM + (maxrows - frows) * XS / 2
  13795.         
  13796.         for suit in range(suits):
  13797.             for i in range(decks):
  13798.                 _.s.foundations.append(S(x, y, suit = suit))
  13799.                 x = x + XS
  13800.             
  13801.         
  13802.         (x, y) = (XM, YM + YS + yextra)
  13803.         _.setRegion(_.s.rows, (-999, y - YM / 2, 999999, 999999))
  13804.         for i in range(rows):
  13805.             _.s.rows.append(S(x, y))
  13806.             x = x + XS
  13807.         
  13808.         _.size = (XM + maxrows * XS, YM + YS + yextra + h)
  13809.  
  13810.     
  13811.     def yukonLayout(_, rows, texts = 0, playcards = 20):
  13812.         S = _._Layout__createStack
  13813.         (CW, CH) = (_.CW, _.CH)
  13814.         (XM, YM) = (_.XM, _.YM)
  13815.         (XS, YS) = (_.XS, _.YS)
  13816.         decks = _.game.gameinfo.decks
  13817.         suits = len(_.game.gameinfo.suits) + bool(_.game.gameinfo.trumps)
  13818.         h = CH * 2 / 3 + (playcards - 1) * _.YOFFSET
  13819.         h = YM + max(h, suits * YS)
  13820.         (x, y) = (XM, YM)
  13821.         for i in range(rows):
  13822.             _.s.rows.append(S(x, y))
  13823.             x = x + XS
  13824.         
  13825.         _.setRegion(_.s.rows, (-999, -999, x - CW / 2, 999999))
  13826.         for suit in range(suits):
  13827.             for i in range(decks):
  13828.                 _.s.foundations.append(S(x + i * XS, y, suit = suit))
  13829.             
  13830.             y = y + YS
  13831.         
  13832.         (x, y) = (XM, h - YS)
  13833.         _.size = (XM + (rows + decks) * XS, h)
  13834.  
  13835.     
  13836.     def easyLayout(_, rows, waste, texts = 1, playcards = 10, center = 1):
  13837.         S = _._Layout__createStack
  13838.         (CW, CH) = (_.CW, _.CH)
  13839.         (XM, YM) = (_.XM, _.YM)
  13840.         (XS, YS) = (_.XS, _.YS)
  13841.         decks = _.game.gameinfo.decks
  13842.         suits = len(_.game.gameinfo.suits) + bool(_.game.gameinfo.trumps)
  13843.         frows = 4 * decks / (1 + (decks >= 3))
  13844.         toprows = 1 + waste + frows
  13845.         maxrows = max(rows, toprows)
  13846.         yextra = 0
  13847.         h = CH * 2 / 3 + (playcards - 1) * _.YOFFSET
  13848.         h = max(h, 2 * YS)
  13849.         (x, y) = (XM, YM)
  13850.         _.s.talon = s = S(x, y)
  13851.         if texts:
  13852.             if waste and not center or maxrows - frows <= 1:
  13853.                 s.setText(x + CW / 2, y + YS, anchor = 'n')
  13854.                 yextra = 20
  13855.             else:
  13856.                 s.setText(x + XS, y, anchor = 'nw', format = '%3d')
  13857.         
  13858.         if waste:
  13859.             x = x + XS
  13860.             _.s.waste = s = S(x, y)
  13861.             if texts:
  13862.                 s.setText(x + CW / 2, y + YS, anchor = 'n')
  13863.             
  13864.         
  13865.         x = XM + (maxrows - frows) * XS
  13866.         if center and frows + 2 * (1 + waste + 1) <= maxrows:
  13867.             x = XM + (maxrows - frows) * XS / 2
  13868.         
  13869.         (x0, y0) = (x, y)
  13870.         for suit in range(suits):
  13871.             for i in range(decks):
  13872.                 _.s.foundations.append(S(x0, y0, suit = suit))
  13873.                 x0 = x0 + XS
  13874.             
  13875.         
  13876.         (x, y) = (XM, y + YS + yextra * (decks <= 2))
  13877.         _.setRegion(_.s.rows, (-999, y - YM / 2, 999999, 999999))
  13878.         for i in range(rows):
  13879.             _.s.rows.append(S(x, y))
  13880.             x = x + XS
  13881.         
  13882.         _.size = (XM + maxrows * XS, YM + YS + yextra + h)
  13883.  
  13884.     
  13885.     def samuriLayout(_, rows, waste, texts = 1, playcards = 20, center = 1):
  13886.         S = _._Layout__createStack
  13887.         (CW, CH) = (_.CW, _.CH)
  13888.         (XM, YM) = (_.XM, _.YM)
  13889.         (XS, YS) = (_.XS, _.YS)
  13890.         decks = _.game.gameinfo.decks
  13891.         suits = len(_.game.gameinfo.suits) + bool(_.game.gameinfo.trumps)
  13892.         toprows = 2 * decks + rows
  13893.         yextra = 0
  13894.         h = CH * 2 / 3 + (playcards - 1) * _.YOFFSET
  13895.         h = max(h, 2 * YS)
  13896.         x = XM + toprows * XS / 2 - XS
  13897.         y = h
  13898.         _.s.talon = s = S(x, y)
  13899.         if texts:
  13900.             if waste and not center or toprows - rows <= 1:
  13901.                 s.setText(x + CW / 2, y + YS, anchor = 'n')
  13902.                 yextra = 20
  13903.             else:
  13904.                 s.setText(x + XS, y, anchor = 'nw', format = '%3d')
  13905.         
  13906.         if waste:
  13907.             x = x + XS
  13908.             _.s.waste = s = S(x, y)
  13909.             if texts:
  13910.                 s.setText(x + CW / 2, y + YS, anchor = 'n')
  13911.             
  13912.         
  13913.         (x, y) = (XM, YM)
  13914.         (d, x0, y0) = (0, x, y)
  13915.         for suit in range(12):
  13916.             for i in range(decks):
  13917.                 (x0, y0) = (x + XS * i, y + YS * d)
  13918.                 _.s.foundations.append(S(x0, y0, suit = suit))
  13919.             
  13920.             d = d + 1
  13921.         
  13922.         (x, y) = (XM + XS * decks, YM)
  13923.         _.setRegion(_.s.rows, (x - XM / 2, 0, x + XS * rows, 999999))
  13924.         for i in range(rows):
  13925.             _.s.rows.append(S(x, y))
  13926.             x = x + XS
  13927.         
  13928.         _.size = (XM + toprows * XS, YM + YS + yextra + h)
  13929.  
  13930.     
  13931.     def sumoLayout(_, rows, reserves, texts = 0, playcards = 12, center = 1):
  13932.         S = _._Layout__createStack
  13933.         (CW, CH) = (_.CW, _.CH)
  13934.         (XM, YM) = (_.XM, _.YM)
  13935.         (XS, YS) = (_.XS, _.YS)
  13936.         decks = _.game.gameinfo.decks
  13937.         suits = len(_.game.gameinfo.suits) + bool(_.game.gameinfo.trumps)
  13938.         if not __debug__ and reserves % 2 == 0:
  13939.             raise AssertionError
  13940.         toprows = 12
  13941.         maxrows = max(rows, toprows)
  13942.         w = XM + maxrows * XS
  13943.         h = CH * 2 / 3 + (playcards - 1) * _.YOFFSET
  13944.         h = max(h, 2 * YS)
  13945.         (x, y) = (XM, YM)
  13946.         for i in range(decks):
  13947.             for suit in range(12):
  13948.                 _.s.foundations.append(S(x, y, suit = suit))
  13949.                 x = x + XS
  13950.             
  13951.             (x, y) = (XM, y + YS)
  13952.         
  13953.         (x, y) = (XM + XS * ((toprows - rows) / 2), YM + YS * decks)
  13954.         for i in range(rows):
  13955.             _.s.rows.append(S(x, y))
  13956.             x = x + XS
  13957.         
  13958.         _.setRegion(_.s.rows, (XS + XM / 2, YS * decks + YM / 2, XS * 11 - XM / 2, 999999))
  13959.         (x, y) = (XM, YM + YS * decks)
  13960.         for i in range(reserves / 2):
  13961.             _.s.reserves.append(S(x, y))
  13962.             y = y + YS
  13963.         
  13964.         (x, y) = (w - XS, YM + YS * decks)
  13965.         for i in range(reserves / 2):
  13966.             _.s.reserves.append(S(x, y))
  13967.             y = y + YS
  13968.         
  13969.         (x, y) = (XM, h)
  13970.         _.size = (XM + toprows * XS, YM + YS + h)
  13971.  
  13972.     
  13973.     def funLayout(_, rows, reserves, texts = 0, playcards = 12, center = 1):
  13974.         S = _._Layout__createStack
  13975.         (CW, CH) = (_.CW, _.CH)
  13976.         (XM, YM) = (_.XM, _.YM)
  13977.         (XS, YS) = (_.XS, _.YS)
  13978.         decks = _.game.gameinfo.decks
  13979.         suits = len(_.game.gameinfo.suits) + bool(_.game.gameinfo.trumps)
  13980.         if not __debug__ and rows % 2 == 0:
  13981.             raise AssertionError
  13982.         if not __debug__ and reserves % decks == 0:
  13983.             raise AssertionError
  13984.         toprows = decks + rows / 2
  13985.         w = XM * 2 + toprows * XS
  13986.         h = CH * 2 / 3 + (playcards - 1) * _.YOFFSET
  13987.         h = max(h, 2 * YS)
  13988.         (x, y) = (w - XS * decks, YM)
  13989.         for i in range(decks):
  13990.             for suit in range(suits):
  13991.                 _.s.foundations.append(S(x, y, suit = suit))
  13992.                 y = y + YS
  13993.             
  13994.             (x, y) = (x + XS, YM)
  13995.         
  13996.         (x, y) = (XM, YM)
  13997.         for i in range(rows / 2):
  13998.             _.s.rows.append(S(x, y))
  13999.             x = x + XS
  14000.         
  14001.         (x, y) = (XM, (YS + h) / 2)
  14002.         for i in range(rows / 2):
  14003.             _.s.rows.append(S(x, y))
  14004.             x = x + XS
  14005.         
  14006.         _.setRegion(_.s.rows, (0, 0, XS * rows / 2 + XM / 2, 999999))
  14007.         (x, y) = (w - XS * decks, YM + YS * 4)
  14008.         for i in range(decks):
  14009.             for i in range(reserves / decks):
  14010.                 _.s.reserves.append(S(x, y))
  14011.                 y = y + YS
  14012.             
  14013.             (x, y) = (x + XS, YM + YS * 4)
  14014.         
  14015.         (x, y) = (XM, h)
  14016.         _.size = (w, YM + YS + h)
  14017.  
  14018.     
  14019.     def oonsooLayout(_, rows, reserves, texts = 0, playcards = 12, center = 1):
  14020.         S = _._Layout__createStack
  14021.         (CW, CH) = (_.CW, _.CH)
  14022.         (XM, YM) = (_.XM, _.YM)
  14023.         (XS, YS) = (_.XS, _.YS)
  14024.         decks = _.game.gameinfo.decks
  14025.         if not __debug__ and rows % 2 == 0:
  14026.             raise AssertionError
  14027.         toprows = decks + rows / 2
  14028.         w = XM * 2 + toprows * (XS + XM)
  14029.         h = CH * 2 / 3 + (playcards - 1) * _.YOFFSET
  14030.         h = max(h, 2 * YS)
  14031.         (x, y) = (XM, YM)
  14032.         _.s.talon = s = S(x, y)
  14033.         if texts:
  14034.             s.setText(x + CW / 2, y + YS, anchor = 'n', format = '%d')
  14035.         
  14036.         (x, y) = (XS + XM * 3, YM)
  14037.         for i in range(rows / 2):
  14038.             _.s.rows.append(S(x, y))
  14039.             x = x + XS + XM
  14040.         
  14041.         (x, y) = (XS + XM * 3, (YS + h) / 2)
  14042.         for i in range(rows / 2):
  14043.             _.s.rows.append(S(x, y))
  14044.             x = x + XS + XM
  14045.         
  14046.         _.setRegion(_.s.rows, (XS + XM, -999, 999999, 999999))
  14047.         (x, y) = (XM, YM * 2 + YS)
  14048.         for i in range(decks):
  14049.             for i in range(reserves / decks):
  14050.                 _.s.reserves.append(S(x, y))
  14051.                 y = y + YS
  14052.             
  14053.             (x, y) = (x + XS, YM + YS * 4)
  14054.         
  14055.         _.size = (w, YM + YS + h)
  14056.  
  14057.     
  14058.     def ghulamLayout(_, rows, reserves = 0, texts = 0):
  14059.         S = _._Layout__createStack
  14060.         (CW, CH) = (_.CW, _.CH)
  14061.         (XM, YM) = (_.XM, _.YM)
  14062.         (XS, YS) = (_.XS, _.YS)
  14063.         decks = _.game.gameinfo.decks
  14064.         if not __debug__ and rows % 2 == 0:
  14065.             raise AssertionError
  14066.         if not __debug__ and reserves % 2 == 0:
  14067.             raise AssertionError
  14068.         (w, h) = (XM * 3 + XS * 9, YM + YS * 6)
  14069.         (x, y) = (XM, YM)
  14070.         for i in range(8):
  14071.             _.s.foundations.append(S(x, y, suit = i))
  14072.             y = y + YS
  14073.         
  14074.         x = XM * 2 + XS
  14075.         for i in range(rows / 2):
  14076.             _.s.rows.append(S(x + i * XS, YM))
  14077.         
  14078.         for i in range(rows / 2):
  14079.             _.s.rows.append(S(x + i * XS, h / 2))
  14080.         
  14081.         _.setRegion(_.s.rows, (XM + XS, -999, w - XM - XS, 999999))
  14082.         for i in range(reserves / 2):
  14083.             _.s.reserves.append(S(XM, h - YS * (i + 1)))
  14084.         
  14085.         for i in range(reserves / 2):
  14086.             _.s.reserves.append(S(w - XS, h - YS * (i + 1)))
  14087.         
  14088.         _.size = (w, h)
  14089.  
  14090.     
  14091.     def generiklonLayout(_, rows, waste = 1, height = 6):
  14092.         S = _._Layout__createStack
  14093.         (CW, CH) = (_.CW, _.CH)
  14094.         (XM, YM) = (_.XM, _.YM)
  14095.         (XS, YS) = (_.XS, _.YS)
  14096.         decks = _.game.gameinfo.decks
  14097.         suits = len(_.game.gameinfo.suits) + bool(_.game.gameinfo.trumps)
  14098.         frows = suits * decks / 2
  14099.         fspace = XS * (rows - 1) / 2
  14100.         (w, h) = (XM + XS * rows, YM * 2 + YS * height)
  14101.         _.size = (w, h)
  14102.         (x, y) = (XM, YM)
  14103.         _.s.talon = s = S(x, y)
  14104.         s.setText(x + XS, y + CH, anchor = 'sw', format = '%3d')
  14105.         _.s.waste = s = S(x, y + YS)
  14106.         s.setText(x + XS, y + YS + CH, anchor = 'sw', format = '%3d')
  14107.         x = w - fspace - XS * frows / 2
  14108.         for suit in range(suits / 2):
  14109.             for i in range(decks):
  14110.                 _.s.foundations.append(S(x, y, suit = suit))
  14111.                 x = x + XS
  14112.             
  14113.         
  14114.         x = w - fspace - XS * frows / 2
  14115.         y = y + YS
  14116.         for suit in range(suits / 2):
  14117.             for i in range(decks):
  14118.                 _.s.foundations.append(S(x, y, suit = suit + suits / 2))
  14119.                 x = x + XS
  14120.             
  14121.         
  14122.         (x, y) = (XM, YM * 2 + YS * 2)
  14123.         for i in range(rows):
  14124.             _.s.rows.append(S(x, y))
  14125.             x = x + XS
  14126.         
  14127.         _.setRegion(_.s.rows, (-999, y - YM, 999999, 999999))
  14128.  
  14129.  
  14130.  
  14131. class Klondike(Game):
  14132.     Layout_Method = Layout.klondikeLayout
  14133.     Talon_Class = WasteTalonStack
  14134.     Foundation_Class = SS_FoundationStack
  14135.     RowStack_Class = KingAC_RowStack
  14136.     Hint_Class = KlondikeType_Hint
  14137.     
  14138.     def createGame(_, max_rounds = -1, num_deal = 1, **layout):
  14139.         (l, s) = (Layout(_), _.s)
  14140.         kwdefault(layout, rows = 7, waste = 1, texts = 1, playcards = 16)
  14141.         apply(_.Layout_Method, (l,), layout)
  14142.         _.setSize(l.size[0], l.size[1])
  14143.         s.talon = _.Talon_Class(l.s.talon.x, l.s.talon.y, _, max_rounds = max_rounds, num_deal = num_deal)
  14144.         if l.s.waste:
  14145.             s.waste = WasteStack(l.s.waste.x, l.s.waste.y, _)
  14146.         
  14147.         for r in l.s.foundations:
  14148.             s.foundations.append(_.Foundation_Class(r.x, r.y, _, suit = r.suit))
  14149.         
  14150.         for r in l.s.rows:
  14151.             s.rows.append(_.RowStack_Class(r.x, r.y, _))
  14152.         
  14153.         l.defaultAll()
  14154.         return l
  14155.  
  14156.     
  14157.     def startGame(_, flip = 0, reverse = 1):
  14158.         for i in range(1, len(_.s.rows)):
  14159.             _.s.talon.dealRow(rows = _.s.rows[i:], flip = flip, frames = 0, reverse = reverse)
  14160.         
  14161.         _.startDealSample()
  14162.         _.s.talon.dealRow(reverse = reverse)
  14163.  
  14164.     
  14165.     def shallHighlightMatch(_, stack1, card1, stack2, card2):
  14166.         if not card1.color != card2.color and card1.rank + 1 == card2.rank:
  14167.             pass
  14168.         return card2.rank + 1 == card1.rank
  14169.  
  14170.  
  14171.  
  14172. class VegasKlondike(Klondike):
  14173.     getGameScore = Game.getGameScoreCasino
  14174.     getGameBalance = Game.getGameScoreCasino
  14175.     
  14176.     def createGame(_, max_rounds = 1):
  14177.         Klondike.createGame(_, max_rounds = max_rounds)
  14178.         _.texts.score = MfxCanvasText(_.canvas, 8, _.height - 8, anchor = 'sw', font = getFont('canvas_large'))
  14179.  
  14180.     
  14181.     def updateText(_):
  14182.         if _.preview > 1:
  14183.             return None
  14184.         
  14185.         (b1, b2) = (_.app.stats.gameid_balance, 0)
  14186.         if _.shallUpdateBalance():
  14187.             b2 = _.getGameBalance()
  14188.         
  14189.         if 0 and _.app.debug:
  14190.             t = 'Balance %d/%d' % (b1, b2)
  14191.         else:
  14192.             t = 'Balance $%d' % (b1 + b2)
  14193.         _.texts.score.config(text = t)
  14194.  
  14195.     
  14196.     def getDemoInfoTextAttr(_, tinfo):
  14197.         return tinfo[1]
  14198.  
  14199.  
  14200.  
  14201. class CasinoKlondike(VegasKlondike):
  14202.     
  14203.     def createGame(_):
  14204.         VegasKlondike.createGame(_, max_rounds = 3)
  14205.  
  14206.  
  14207.  
  14208. class KlondikeByThrees(Klondike):
  14209.     
  14210.     def createGame(_):
  14211.         Klondike.createGame(_, num_deal = 3)
  14212.  
  14213.  
  14214.  
  14215. class ThumbAndPouch_RowStack(SequenceRowStack):
  14216.     
  14217.     def _isSequence(_, cards):
  14218.         return isAnySuitButOwnSequence(cards, _.cap.mod, _.cap.dir)
  14219.  
  14220.  
  14221.  
  14222. class ThumbAndPouch(Klondike):
  14223.     RowStack_Class = ThumbAndPouch_RowStack
  14224.     
  14225.     def createGame(_):
  14226.         Klondike.createGame(_, max_rounds = 1)
  14227.  
  14228.     
  14229.     def shallHighlightMatch(_, stack1, card1, stack2, card2):
  14230.         if not card1.suit != card2.suit and card1.rank + 1 == card2.rank:
  14231.             pass
  14232.         return card2.rank + 1 == card1.rank
  14233.  
  14234.  
  14235.  
  14236. class Whitehead_RowStack(SS_RowStack):
  14237.     
  14238.     def _isAcceptableSequence(_, cards):
  14239.         return isSameColorSequence(cards, _.cap.mod, _.cap.dir)
  14240.  
  14241.  
  14242.  
  14243. class Whitehead(Klondike):
  14244.     RowStack_Class = Whitehead_RowStack
  14245.     Hint_Class = CautiousDefaultHint
  14246.     
  14247.     def createGame(_):
  14248.         Klondike.createGame(_, max_rounds = 1)
  14249.  
  14250.     
  14251.     def startGame(_):
  14252.         Klondike.startGame(_, flip = 1)
  14253.  
  14254.     
  14255.     def shallHighlightMatch(_, stack1, card1, stack2, card2):
  14256.         if not card1.suit == card2.suit and card1.rank + 1 == card2.rank:
  14257.             pass
  14258.         return card2.rank + 1 == card1.rank
  14259.  
  14260.  
  14261.  
  14262. class SmallHarp(Klondike):
  14263.     Layout_Method = Layout.gypsyLayout
  14264.     
  14265.     def startGame(_):
  14266.         for i in range(len(_.s.rows)):
  14267.             _.s.talon.dealRow(rows = _.s.rows[:i], flip = 0, frames = 0)
  14268.         
  14269.         _.startDealSample()
  14270.         _.s.talon.dealRow()
  14271.         _.s.talon.dealCards()
  14272.  
  14273.  
  14274.  
  14275. class Eastcliff(Klondike):
  14276.     RowStack_Class = AC_RowStack
  14277.     
  14278.     def createGame(_):
  14279.         Klondike.createGame(_, max_rounds = 1)
  14280.  
  14281.     
  14282.     def startGame(_):
  14283.         for i in range(2):
  14284.             _.s.talon.dealRow(flip = 0, frames = 0)
  14285.         
  14286.         _.startDealSample()
  14287.         _.s.talon.dealRow()
  14288.  
  14289.  
  14290.  
  14291. class Easthaven(Eastcliff):
  14292.     Talon_Class = DealRowTalonStack
  14293.     
  14294.     def createGame(_):
  14295.         Klondike.createGame(_, max_rounds = 1, waste = 0)
  14296.  
  14297.  
  14298.  
  14299. class Westcliff(Eastcliff):
  14300.     Foundation_Class = StackWrapper(SS_FoundationStack, max_move = 0)
  14301.     
  14302.     def createGame(_):
  14303.         Klondike.createGame(_, max_rounds = 1, rows = 10)
  14304.  
  14305.  
  14306.  
  14307. class Westhaven(Westcliff):
  14308.     Talon_Class = DealRowTalonStack
  14309.     
  14310.     def createGame(_):
  14311.         Klondike.createGame(_, max_rounds = 1, rows = 10, waste = 0)
  14312.  
  14313.  
  14314.  
  14315. class PasSeul(Eastcliff):
  14316.     
  14317.     def createGame(_):
  14318.         Klondike.createGame(_, max_rounds = 1, rows = 6)
  14319.  
  14320.     
  14321.     def startGame(_):
  14322.         _.startDealSample()
  14323.         _.s.talon.dealRow()
  14324.         _.s.talon.dealCards()
  14325.  
  14326.  
  14327.  
  14328. class BlindAlleys(Eastcliff):
  14329.     
  14330.     def createGame(_):
  14331.         Klondike.createGame(_, max_rounds = 2, rows = 6)
  14332.  
  14333.     
  14334.     def _shuffleHook(_, cards):
  14335.         return _._shuffleHookMoveToTop(cards, (lambda c: (c.rank == 0, c.suit)))
  14336.  
  14337.     
  14338.     def startGame(_):
  14339.         _.s.talon.dealRow(rows = _.s.foundations, frames = 0)
  14340.         Eastcliff.startGame(_)
  14341.  
  14342.  
  14343.  
  14344. class Somerset(Klondike):
  14345.     Talon_Class = InitialDealTalonStack
  14346.     RowStack_Class = StackWrapper(AC_RowStack, max_move = 1)
  14347.     Hint_Class = CautiousDefaultHint
  14348.     
  14349.     def createGame(_):
  14350.         Klondike.createGame(_, max_rounds = 1, rows = 10, waste = 0, texts = 0)
  14351.  
  14352.     
  14353.     def startGame(_):
  14354.         for i in range(6):
  14355.             _.s.talon.dealRow(rows = _.s.rows[i:], frames = 0)
  14356.         
  14357.         _.startDealSample()
  14358.         _.s.talon.dealRow(rows = _.s.rows[6:])
  14359.         _.s.talon.dealRow(rows = _.s.rows[7:])
  14360.  
  14361.  
  14362.  
  14363. class Canister(Klondike):
  14364.     Talon_Class = InitialDealTalonStack
  14365.     RowStack_Class = RK_RowStack
  14366.     
  14367.     def createGame(_):
  14368.         Klondike.createGame(_, max_rounds = 1, rows = 8, waste = 0, texts = 0)
  14369.  
  14370.     
  14371.     def startGame(_):
  14372.         for i in range(5):
  14373.             _.s.talon.dealRow(frames = 0)
  14374.         
  14375.         _.startDealSample()
  14376.         _.s.talon.dealRow()
  14377.         _.s.talon.dealRow(rows = _.s.rows[2:6])
  14378.  
  14379.  
  14380.  
  14381. class AgnesSorel(Klondike):
  14382.     Talon_Class = DealRowTalonStack
  14383.     Foundation_Class = StackWrapper(SS_FoundationStack, mod = 13, base_rank = NO_RANK, max_move = 0)
  14384.     RowStack_Class = StackWrapper(SC_RowStack, mod = 13, base_rank = NO_RANK)
  14385.     
  14386.     def createGame(_):
  14387.         Klondike.createGame(_, max_rounds = 1, waste = 0)
  14388.  
  14389.     
  14390.     def startGame(_):
  14391.         Klondike.startGame(_, flip = 1)
  14392.         c = _.s.talon.dealSingleBaseCard()
  14393.  
  14394.     
  14395.     def shallHighlightMatch(_, stack1, card1, stack2, card2):
  14396.         if not card1.color == card2.color and (card1.rank + 1) % 13 == card2.rank:
  14397.             pass
  14398.         return (card2.rank + 1) % 13 == card1.rank
  14399.  
  14400.  
  14401.  
  14402. class EightTimesEight(Klondike):
  14403.     Layout_Method = Layout.gypsyLayout
  14404.     RowStack_Class = AC_RowStack
  14405.     
  14406.     def createGame(_):
  14407.         Klondike.createGame(_, rows = 8)
  14408.  
  14409.     
  14410.     def startGame(_):
  14411.         for i in range(7):
  14412.             _.s.talon.dealRow(frames = 0)
  14413.         
  14414.         _.startDealSample()
  14415.         _.s.talon.dealRow()
  14416.         _.s.talon.dealCards()
  14417.  
  14418.  
  14419.  
  14420. class AchtmalAcht(EightTimesEight):
  14421.     
  14422.     def createGame(_):
  14423.         l = Klondike.createGame(_, rows = 8, max_rounds = 3)
  14424.         s = _.s
  14425.         (x, y) = (s.waste.x - l.XM, s.waste.y)
  14426.         s.talon.texts.rounds = MfxCanvasText(_.canvas, x, y, anchor = 'ne')
  14427.  
  14428.  
  14429.  
  14430. class Batsford_ReserveStack(ReserveStack):
  14431.     
  14432.     def acceptsCards(_, from_stack, cards):
  14433.         if not ReserveStack.acceptsCards(_, from_stack, cards):
  14434.             return 0
  14435.         
  14436.         return cards[0].rank == KING
  14437.  
  14438.  
  14439.  
  14440. class Batsford(Klondike):
  14441.     
  14442.     def createGame(_, **layout):
  14443.         l = Klondike.createGame(_, rows = 10, max_rounds = 1, playcards = 22)
  14444.         s = _.s
  14445.         (x, y) = (l.XM, _.height - l.YS)
  14446.         s.reserves.append(Batsford_ReserveStack(x, y, _, max_cards = 3))
  14447.         _.setRegion(s.reserves, (-999, y - l.YM, x + l.XS, 999999), priority = 1)
  14448.         l.createText(s.reserves[0], 'se')
  14449.         l.defaultStackGroups()
  14450.  
  14451.  
  14452.  
  14453. class Stonewall(Klondike):
  14454.     Talon_Class = InitialDealTalonStack
  14455.     RowStack_Class = AC_RowStack
  14456.     DEAL = (0, 1, 0, 1, -1, 0, 1)
  14457.     
  14458.     def createGame(_):
  14459.         l = Klondike.createGame(_, rows = 6, max_rounds = 1, texts = 0)
  14460.         s = _.s
  14461.         h = max(_.height, l.YM + 4 * l.YS)
  14462.         _.setSize(_.width + l.XM + 4 * l.XS, h)
  14463.         for i in range(4):
  14464.             for j in range(4):
  14465.                 (x, y) = (_.width + (j - 4) * l.XS, l.YM + i * l.YS)
  14466.                 s.reserves.append(OpenStack(x, y, _, max_accept = 0))
  14467.             
  14468.         
  14469.         l.defaultStackGroups()
  14470.  
  14471.     
  14472.     def startGame(_):
  14473.         frames = 0
  14474.         for flip in _.DEAL:
  14475.             pass
  14476.         
  14477.         _.s.talon.dealRow(rows = _.s.reserves)
  14478.         if not __debug__ and len(_.s.talon.cards) == 0:
  14479.             raise AssertionError
  14480.         None if flip < 0 else _.DEAL
  14481.  
  14482.  
  14483.  
  14484. class FlowerGarden(Stonewall):
  14485.     RowStack_Class = StackWrapper(RK_RowStack, max_move = 1)
  14486.     Hint_Class = CautiousDefaultHint
  14487.     DEAL = (1, 1, 1, 1, -1, 1, 1)
  14488.  
  14489.  
  14490. class KingAlbert(Klondike):
  14491.     Talon_Class = InitialDealTalonStack
  14492.     RowStack_Class = StackWrapper(AC_RowStack, max_move = 1)
  14493.     Hint_Class = CautiousDefaultHint
  14494.     ROWS = 9
  14495.     RESERVES = (2, 2, 2, 1)
  14496.     
  14497.     def createGame(_):
  14498.         l = Klondike.createGame(_, max_rounds = 1, rows = _.ROWS, waste = 0, texts = 0)
  14499.         s = _.s
  14500.         (rw, rh) = (max(_.RESERVES), len(_.RESERVES))
  14501.         h = max(_.height, l.YM + rh * l.YS)
  14502.         _.setSize(_.width + 2 * l.XM + rw * l.XS, h)
  14503.         for i in range(rh):
  14504.             for j in range(_.RESERVES[i]):
  14505.                 (x, y) = (_.width + (j - rw) * l.XS, l.YM + i * l.YS)
  14506.                 s.reserves.append(OpenStack(x, y, _, max_accept = 0))
  14507.             
  14508.         
  14509.         l.defaultStackGroups()
  14510.  
  14511.     
  14512.     def startGame(_):
  14513.         Klondike.startGame(_, flip = 1, reverse = 0)
  14514.         _.s.talon.dealRow(rows = _.s.reserves)
  14515.  
  14516.  
  14517.  
  14518. class Raglan(KingAlbert):
  14519.     RESERVES = (2, 2, 2)
  14520.     
  14521.     def _shuffleHook(_, cards):
  14522.         return _._shuffleHookMoveToBottom(cards, (lambda c: (c.rank == 0, c.suit)))
  14523.  
  14524.     
  14525.     def startGame(_):
  14526.         for i in range(6):
  14527.             _.s.talon.dealRow(rows = _.s.rows[i:], frames = 0)
  14528.         
  14529.         _.startDealSample()
  14530.         _.s.talon.dealRow(rows = _.s.rows[6:])
  14531.         _.s.talon.dealRow(rows = _.s.reserves)
  14532.         _.s.talon.dealRow(rows = _.s.foundations)
  14533.  
  14534.  
  14535.  
  14536. class Brigade(Raglan):
  14537.     RowStack_Class = StackWrapper(RK_RowStack, max_move = 1)
  14538.     ROWS = 7
  14539.     RESERVES = (4, 4, 4, 1)
  14540.     
  14541.     def startGame(_):
  14542.         for i in range(4):
  14543.             _.s.talon.dealRow(frames = 0)
  14544.         
  14545.         _.startDealSample()
  14546.         _.s.talon.dealRow()
  14547.         _.s.talon.dealRow(rows = _.s.reserves)
  14548.         _.s.talon.dealRow(rows = _.s.foundations)
  14549.  
  14550.     
  14551.     def shallHighlightMatch(_, stack1, card1, stack2, card2):
  14552.         if not card1.rank + 1 == card2.rank:
  14553.             pass
  14554.         return card2.rank + 1 == card1.rank
  14555.  
  14556.  
  14557.  
  14558. class Jane_Talon(OpenTalonStack):
  14559.     
  14560.     def canFlipCard(_):
  14561.         return 0
  14562.  
  14563.     
  14564.     def canDealCards(_):
  14565.         return len(_.cards) >= 2
  14566.  
  14567.     
  14568.     def dealCards(_, sound = 0):
  14569.         c = 0
  14570.         if len(_.cards) > 2:
  14571.             c = _.dealRow(_.game.s.reserves, sound = sound)
  14572.         
  14573.         if len(_.cards) == 2:
  14574.             _.game.flipMove(_)
  14575.             _.game.moveMove(1, _, _.game.s.waste, frames = 4, shadow = 0)
  14576.             _.game.flipMove(_)
  14577.             c = c + 1
  14578.         
  14579.         return c
  14580.  
  14581.  
  14582.  
  14583. class Jane(Klondike):
  14584.     Talon_Class = Jane_Talon
  14585.     Foundation_Class = StackWrapper(SS_FoundationStack, mod = 13, base_rank = NO_RANK, min_cards = 1)
  14586.     RowStack_Class = StackWrapper(AC_RowStack, mod = 13, base_rank = NO_RANK)
  14587.     
  14588.     def createGame(_, max_rounds = 1, reserves = 7, **layout):
  14589.         kwdefault(layout, texts = 0)
  14590.         l = apply(Klondike.createGame, (_, max_rounds), layout)
  14591.         s = _.s
  14592.         h = max(_.height, l.YM + 4 * l.YS)
  14593.         _.setSize(_.width + l.XM + 2 * l.XS, h)
  14594.         (x0, y) = (_.width - 2 * l.XS, l.YM)
  14595.         for i in range(reserves):
  14596.             x = x0 + (i + 1 & 1) * l.XS
  14597.             stack = OpenStack(x, y, _, max_accept = 0)
  14598.             stack.CARD_YOFFSET = l.YM / 3
  14599.             stack.is_open = 1
  14600.             s.reserves.append(stack)
  14601.             y = y + l.YS / 2
  14602.         
  14603.         l.defaultStackGroups()
  14604.         _.sg.dropstacks.append(s.talon)
  14605.         (x, y) = (l.XM, _.height - l.YM)
  14606.         _.texts.info = MfxCanvasText(_.canvas, x, y, anchor = 'sw', font = getFont('canvas_card', cardw = l.CW))
  14607.  
  14608.     
  14609.     def startGame(_, flip = 0, reverse = 1):
  14610.         for i in range(1, len(_.s.rows)):
  14611.             _.s.talon.dealRow(rows = _.s.rows[i:], flip = flip, frames = 0, reverse = reverse)
  14612.         
  14613.         _.startDealSample()
  14614.         _.s.talon.dealRow(reverse = reverse)
  14615.         _.s.talon.dealRow(rows = _.s.reserves)
  14616.         c = _.s.talon.dealSingleBaseCard()
  14617.         cap = Struct(base_rank = (c.rank - 1) % 13)
  14618.         for s in _.s.rows:
  14619.             s.cap.update(cap.__dict__)
  14620.             _.saveinfo.stack_caps.append((s.id, cap))
  14621.         
  14622.  
  14623.     
  14624.     def shallHighlightMatch(_, stack1, card1, stack2, card2):
  14625.         if not card1.suit == card2.suit and (card1.rank + 1) % 13 == card2.rank:
  14626.             pass
  14627.         return (card2.rank + 1) % 13 == card1.rank
  14628.  
  14629.     
  14630.     def _autoDeal(_, sound = 1):
  14631.         return 0
  14632.  
  14633.  
  14634.  
  14635. class AgnesBernauer_Talon(DealRowTalonStack):
  14636.     
  14637.     def dealCards(_, sound = 0):
  14638.         return _.dealRowAvail(_.game.s.reserves, sound = sound)
  14639.  
  14640.  
  14641.  
  14642. class AgnesBernauer(Jane):
  14643.     Talon_Class = AgnesBernauer_Talon
  14644.     Foundation_Class = StackWrapper(SS_FoundationStack, mod = 13, base_rank = NO_RANK, max_move = 0)
  14645.     
  14646.     def createGame(_):
  14647.         Jane.createGame(_, max_rounds = 1, waste = 0, texts = 1)
  14648.  
  14649.     
  14650.     def startGame(_):
  14651.         Jane.startGame(_, flip = 1)
  14652.  
  14653.  
  14654. registerGame(GameInfo(2, Klondike, 'Klondike', GI.GT_KLONDIKE, 1, -1))
  14655. registerGame(GameInfo(61, CasinoKlondike, 'Casino Klondike', GI.GT_KLONDIKE | GI.GT_SCORE, 1, 2))
  14656. registerGame(GameInfo(129, VegasKlondike, 'Vegas Klondike', GI.GT_KLONDIKE | GI.GT_SCORE, 1, 0))
  14657. registerGame(GameInfo(18, KlondikeByThrees, 'Klondike by Threes', GI.GT_KLONDIKE, 1, -1))
  14658. registerGame(GameInfo(58, ThumbAndPouch, 'Thumb and Pouch', GI.GT_KLONDIKE, 1, 0))
  14659. registerGame(GameInfo(67, Whitehead, 'Whitehead', GI.GT_KLONDIKE, 1, 0))
  14660. registerGame(GameInfo(39, SmallHarp, 'Small Harp', GI.GT_KLONDIKE, 1, -1))
  14661. registerGame(GameInfo(66, Eastcliff, 'Eastcliff', GI.GT_KLONDIKE, 1, 0))
  14662. registerGame(GameInfo(224, Easthaven, 'Easthaven', GI.GT_GYPSY, 1, 0))
  14663. registerGame(GameInfo(33, Westcliff, 'Westcliff', GI.GT_KLONDIKE, 1, 0))
  14664. registerGame(GameInfo(225, Westhaven, 'Westhaven', GI.GT_GYPSY, 1, 0))
  14665. registerGame(GameInfo(107, PasSeul, 'Pas Seul', GI.GT_KLONDIKE, 1, 0))
  14666. registerGame(GameInfo(81, BlindAlleys, 'Blind Alleys', GI.GT_KLONDIKE, 1, 1))
  14667. registerGame(GameInfo(215, Somerset, 'Somerset', GI.GT_KLONDIKE | GI.GT_OPEN, 1, 0))
  14668. registerGame(GameInfo(231, Canister, 'Canister', GI.GT_KLONDIKE | GI.GT_OPEN, 1, 0))
  14669. registerGame(GameInfo(229, AgnesSorel, 'Agnes Sorel', GI.GT_GYPSY, 1, 0))
  14670. registerGame(GameInfo(4, EightTimesEight, '8 x 8', GI.GT_KLONDIKE, 2, -1))
  14671. registerGame(GameInfo(127, AchtmalAcht, 'Achtmal Acht', GI.GT_KLONDIKE, 2, 2))
  14672. registerGame(GameInfo(133, Batsford, 'Batsford', GI.GT_KLONDIKE, 2, 0))
  14673. registerGame(GameInfo(221, Stonewall, 'Stonewall', GI.GT_RAGLAN, 1, 0))
  14674. registerGame(GameInfo(222, FlowerGarden, 'Flower Garden', GI.GT_RAGLAN | GI.GT_OPEN, 1, 0))
  14675. registerGame(GameInfo(233, KingAlbert, 'King Albert', GI.GT_RAGLAN | GI.GT_OPEN, 1, 0))
  14676. registerGame(GameInfo(232, Raglan, 'Raglan', GI.GT_RAGLAN | GI.GT_OPEN, 1, 0))
  14677. registerGame(GameInfo(223, Brigade, 'Brigade', GI.GT_RAGLAN | GI.GT_OPEN, 1, 0))
  14678. registerGame(GameInfo(230, Jane, 'Jane', GI.GT_RAGLAN, 1, 0))
  14679. registerGame(GameInfo(236, AgnesBernauer, 'Agnes Bernauer', GI.GT_RAGLAN, 1, 0))
  14680.  
  14681. class Gypsy(Game):
  14682.     Layout_Method = Layout.gypsyLayout
  14683.     Talon_Class = DealRowTalonStack
  14684.     Foundation_Class = SS_FoundationStack
  14685.     RowStack_Class = AC_RowStack
  14686.     Hint_Class = KlondikeType_Hint
  14687.     
  14688.     def createGame(_, **layout):
  14689.         (l, s) = (Layout(_), _.s)
  14690.         kwdefault(layout, rows = 8, waste = 0, texts = 1)
  14691.         apply(_.Layout_Method, (l,), layout)
  14692.         _.setSize(l.size[0], l.size[1])
  14693.         s.talon = _.Talon_Class(l.s.talon.x, l.s.talon.y, _)
  14694.         if l.s.waste:
  14695.             s.waste = WasteStack(l.s.waste.x, l.s.waste.y, _)
  14696.         
  14697.         for r in l.s.foundations:
  14698.             s.foundations.append(_.Foundation_Class(r.x, r.y, _, suit = r.suit))
  14699.         
  14700.         for r in l.s.rows:
  14701.             s.rows.append(_.RowStack_Class(r.x, r.y, _))
  14702.         
  14703.         l.defaultAll()
  14704.  
  14705.     
  14706.     def startGame(_):
  14707.         for i in range(2):
  14708.             _.s.talon.dealRow(flip = 0, frames = 0)
  14709.         
  14710.         _.startDealSample()
  14711.         _.s.talon.dealRow()
  14712.  
  14713.     
  14714.     def shallHighlightMatch(_, stack1, card1, stack2, card2):
  14715.         if not card1.color != card2.color and card1.rank + 1 == card2.rank:
  14716.             pass
  14717.         return card2.rank + 1 == card1.rank
  14718.  
  14719.  
  14720.  
  14721. class Giant_Foundation(SS_FoundationStack):
  14722.     
  14723.     def canMoveCards(_, cards):
  14724.         if not SS_FoundationStack.canMoveCards(_, cards):
  14725.             return 0
  14726.         
  14727.         return len(_.game.s.talon.cards) == 0
  14728.  
  14729.  
  14730.  
  14731. class Giant(Gypsy):
  14732.     Foundation_Class = Giant_Foundation
  14733.     
  14734.     def startGame(_):
  14735.         _.startDealSample()
  14736.         _.s.talon.dealRow()
  14737.  
  14738.  
  14739.  
  14740. class Irmgard_Talon(TalonStack):
  14741.     
  14742.     def dealCards(_, sound = 0):
  14743.         if _.cards:
  14744.             if len(_.cards) > 7:
  14745.                 c = _.dealRow(sound = sound)
  14746.             else:
  14747.                 c = _.dealRow(_.game.s.rows[1:8], sound = sound)
  14748.             return c
  14749.         
  14750.         return 0
  14751.  
  14752.  
  14753.  
  14754. class Irmgard(Gypsy):
  14755.     Layout_Method = Layout.harpLayout
  14756.     Talon_Class = Irmgard_Talon
  14757.     
  14758.     def createGame(_):
  14759.         Gypsy.createGame(_, rows = 9, playcards = 19)
  14760.  
  14761.     
  14762.     def startGame(_):
  14763.         r = _.s.rows
  14764.         for i in range(1, 5):
  14765.             _.s.talon.dealRow(rows = r[i:len(r) - i], flip = 0, frames = 0)
  14766.         
  14767.         _.startDealSample()
  14768.         _.s.talon.dealRow()
  14769.  
  14770.  
  14771.  
  14772. class DieKoenigsbergerin_Talon(DealRowTalonStack):
  14773.     dealToStacks = DealRowTalonStack.dealToStacksOrFoundations
  14774.  
  14775.  
  14776. class DieKoenigsbergerin(Gypsy):
  14777.     Talon_Class = DieKoenigsbergerin_Talon
  14778.     Foundation_Class = StackWrapper(SS_FoundationStack, max_move = 0)
  14779.     
  14780.     def startGame(_):
  14781.         _.startDealSample()
  14782.         for i in range(3):
  14783.             _.s.talon.dealRow()
  14784.         
  14785.  
  14786.  
  14787.  
  14788. class DieRussische_Foundation(AbstractFoundationStack):
  14789.     
  14790.     def acceptsCards(_, from_stack, cards):
  14791.         if not AbstractFoundationStack.acceptsCards(_, from_stack, cards):
  14792.             return 0
  14793.         
  14794.         if _.cards:
  14795.             rank = _.cards[-1].rank
  14796.             if rank == ACE:
  14797.                 rank = 5
  14798.             
  14799.             if (rank + _.cap.dir) % _.cap.mod != cards[0].rank:
  14800.                 return 0
  14801.             
  14802.         
  14803.         return 1
  14804.  
  14805.  
  14806.  
  14807. class DieRussische_RowStack(AC_RowStack):
  14808.     
  14809.     def acceptsCards(_, from_stack, cards):
  14810.         if not AC_RowStack.acceptsCards(_, from_stack, cards):
  14811.             return 0
  14812.         
  14813.         if not _.cards:
  14814.             pass
  14815.         return len(cards) == 1
  14816.  
  14817.  
  14818.  
  14819. class DieRussische(Gypsy):
  14820.     Talon_Class = InitialDealTalonStack
  14821.     Foundation_Class = StackWrapper(DieRussische_Foundation, min_cards = 1)
  14822.     RowStack_Class = DieRussische_RowStack
  14823.     
  14824.     def createGame(_):
  14825.         Gypsy.createGame(_, rows = 7, texts = 0)
  14826.  
  14827.     
  14828.     def _shuffleHook(_, cards):
  14829.         return _._shuffleHookMoveToBottom(cards, (lambda c: (c.rank == 0, c.suit)), 1)
  14830.  
  14831.     
  14832.     def startGame(_):
  14833.         for i in range(6):
  14834.             _.s.talon.dealRow(frames = 0)
  14835.         
  14836.         _.startDealSample()
  14837.         for i in range(3):
  14838.             _.s.talon.dealRow()
  14839.         
  14840.         c = _.s.talon.cards[-1]
  14841.         _.s.talon.dealRow(rows = (_.s.foundations[c.suit * 2],))
  14842.  
  14843.  
  14844.  
  14845. class MissMilligan_ReserveStack(AC_RowStack):
  14846.     
  14847.     def acceptsCards(_, from_stack, cards):
  14848.         if not AC_RowStack.acceptsCards(_, from_stack, cards):
  14849.             return 0
  14850.         
  14851.         if len(_.cards) == 0:
  14852.             pass
  14853.         return len(_.game.s.talon.cards) == 0
  14854.  
  14855.     
  14856.     def getBottomImage(_):
  14857.         return _.game.app.images.getReserveBottom()
  14858.  
  14859.  
  14860.  
  14861. class MissMilligan(Gypsy):
  14862.     Foundation_Class = StackWrapper(SS_FoundationStack, max_move = 0)
  14863.     RowStack_Class = KingAC_RowStack
  14864.     ReserveStack_Class = MissMilligan_ReserveStack
  14865.     
  14866.     def createGame(_, rows = 8, reserves = 1):
  14867.         (l, s) = (Layout(_), _.s)
  14868.         _.setSize(l.XM + (1 + max(8, rows)) * l.XS, l.YM + (1 + max(4, reserves)) * l.YS)
  14869.         (x, y) = (l.XM, l.YM)
  14870.         s.talon = _.Talon_Class(x, y, _)
  14871.         for i in range(8):
  14872.             x = x + l.XS
  14873.             s.foundations.append(_.Foundation_Class(x, y, _, suit = i / 2))
  14874.         
  14875.         (x, y) = (l.XM, y + l.YS)
  14876.         (rx, ry) = (x + l.XS - l.XM / 2, y - l.YM / 2)
  14877.         for i in range(reserves):
  14878.             s.reserves.append(_.ReserveStack_Class(x, y, _))
  14879.             y = y + l.YS
  14880.         
  14881.         (x, y) = (l.XM + (8 - rows) * l.XS / 2, l.YM + l.YS)
  14882.         for i in range(rows):
  14883.             x = x + l.XS
  14884.             s.rows.append(_.RowStack_Class(x, y, _))
  14885.         
  14886.         _.setRegion(s.rows, (rx, ry, 999999, 999999))
  14887.         l.defaultStackGroups()
  14888.  
  14889.     
  14890.     def startGame(_):
  14891.         _.startDealSample()
  14892.         _.s.talon.dealRow()
  14893.  
  14894.  
  14895.  
  14896. class Nomad(MissMilligan):
  14897.     Foundation_Class = SS_FoundationStack
  14898.     RowStack_Class = AC_RowStack
  14899.     ReserveStack_Class = ReserveStack
  14900.     
  14901.     def startGame(_):
  14902.         for i in range(3):
  14903.             _.s.talon.dealRow(frames = 0)
  14904.         
  14905.         _.startDealSample()
  14906.         _.s.talon.dealRow()
  14907.  
  14908.  
  14909.  
  14910. class MilliganCell(MissMilligan):
  14911.     ReserveStack_Class = ReserveStack
  14912.     
  14913.     def createGame(_):
  14914.         MissMilligan.createGame(_, reserves = 4)
  14915.  
  14916.     
  14917.     def startGame(_):
  14918.         _.startDealSample()
  14919.         _.s.talon.dealRow()
  14920.  
  14921.  
  14922.  
  14923. class MilliganHarp(Gypsy):
  14924.     Foundation_Class = StackWrapper(SS_FoundationStack, max_move = 0)
  14925.     
  14926.     def startGame(_, flip = 0):
  14927.         for i in range(len(_.s.rows)):
  14928.             _.s.talon.dealRow(rows = _.s.rows[i + 1:], flip = flip, frames = 0)
  14929.         
  14930.         _.startDealSample()
  14931.         _.s.talon.dealRow()
  14932.  
  14933.  
  14934.  
  14935. class Carlton(MilliganHarp):
  14936.     
  14937.     def startGame(_):
  14938.         MilliganHarp.startGame(_, flip = 1)
  14939.  
  14940.  
  14941.  
  14942. class LexingtonHarp(MilliganHarp):
  14943.     GAME_VERSION = 2
  14944.     RowStack_Class = Yukon_AC_RowStack
  14945.     Hint_Class = YukonType_Hint
  14946.  
  14947.  
  14948. class Brunswick(LexingtonHarp):
  14949.     
  14950.     def startGame(_):
  14951.         LexingtonHarp.startGame(_, flip = 1)
  14952.  
  14953.  
  14954.  
  14955. class Mississippi(LexingtonHarp):
  14956.     
  14957.     def createGame(_):
  14958.         LexingtonHarp.createGame(_, rows = 7)
  14959.  
  14960.  
  14961.  
  14962. class Griffon(Mississippi):
  14963.     
  14964.     def startGame(_):
  14965.         Mississippi.startGame(_, flip = 1)
  14966.  
  14967.  
  14968.  
  14969. class Blockade(Gypsy):
  14970.     Layout_Method = Layout.klondikeLayout
  14971.     RowStack_Class = SS_RowStack
  14972.     
  14973.     def createGame(_):
  14974.         Gypsy.createGame(_, rows = 12)
  14975.  
  14976.     
  14977.     def startGame(_):
  14978.         _.startDealSample()
  14979.         _.s.talon.dealRow()
  14980.  
  14981.     
  14982.     def fillStack(_, stack):
  14983.         if stack in _.s.rows and not (stack.cards) and _.s.talon.cards:
  14984.             old_state = _.enterState(_.S_FILL)
  14985.             _.s.talon.flipMove()
  14986.             _.s.talon.moveMove(1, stack)
  14987.             _.leaveState(old_state)
  14988.         
  14989.  
  14990.  
  14991. registerGame(GameInfo(1, Gypsy, 'Gypsy', GI.GT_GYPSY, 2, 0))
  14992. registerGame(GameInfo(65, Giant, 'Giant', GI.GT_GYPSY, 2, 0))
  14993. registerGame(GameInfo(3, Irmgard, 'Irmgard', GI.GT_GYPSY, 2, 0))
  14994. registerGame(GameInfo(119, DieKoenigsbergerin, 'Die K\xf6nigsbergerin', GI.GT_GYPSY, 2, 0))
  14995. registerGame(GameInfo(174, DieRussische, 'Die Russische', GI.GT_2DECK_TYPE | GI.GT_OPEN, 2, 0, ranks = (0, 6, 7, 8, 9, 10, 11, 12)))
  14996. registerGame(GameInfo(62, MissMilligan, 'Miss Milligan', GI.GT_GYPSY, 2, 0))
  14997. registerGame(GameInfo(200, Nomad, 'Nomad', GI.GT_GYPSY | GI.GT_CONTRIB | GI.GT_ORIGINAL, 2, 0))
  14998. registerGame(GameInfo(78, MilliganCell, 'Milligan Cell', GI.GT_GYPSY, 2, 0))
  14999. registerGame(GameInfo(217, MilliganHarp, 'Milligan Harp', GI.GT_GYPSY, 2, 0))
  15000. registerGame(GameInfo(218, Carlton, 'Carlton', GI.GT_GYPSY, 2, 0))
  15001. registerGame(GameInfo(68, LexingtonHarp, 'Lexington Harp', GI.GT_YUKON, 2, 0))
  15002. registerGame(GameInfo(154, Brunswick, 'Brunswick', GI.GT_YUKON, 2, 0))
  15003. registerGame(GameInfo(226, Blockade, 'Blockade', GI.GT_GYPSY, 2, 0))
  15004.  
  15005. class FortyThieves_Hint(CautiousDefaultHint):
  15006.     pass
  15007.  
  15008.  
  15009. class FortyThieves(Game):
  15010.     Foundation_Class = SS_FoundationStack
  15011.     RowStack_Class = SS_RowStack
  15012.     Hint_Class = FortyThieves_Hint
  15013.     FOUNDATION_MAX_MOVE = 1
  15014.     ROW_MAX_MOVE = 1
  15015.     DEAL = (0, 4)
  15016.     FILL_EMPTY_ROWS = 0
  15017.     
  15018.     def createGame(_, max_rounds = 1, num_deal = 1, rows = 10, playcards = 12, XCARDS = 64, XOFFSET = 10):
  15019.         XM = (10, 4)[rows > 10]
  15020.         (l, s) = (Layout(_, XM = XM, XOFFSET = XOFFSET, YBOTTOM = 16), _.s)
  15021.         decks = _.gameinfo.decks
  15022.         maxrows = max(rows, 4 * decks + 2)
  15023.         (w1, w2) = (maxrows * l.XS + l.XM, 2 * l.XS)
  15024.         if w2 + XCARDS * l.XOFFSET > w1:
  15025.             l.XOFFSET = int((w1 - w2) / XCARDS)
  15026.         
  15027.         h = max(2 * l.YS, l.YS + (playcards - 1) * l.YOFFSET)
  15028.         _.setSize(w1, l.YM + l.YS + h + l.YS + l.YBOTTOM)
  15029.         x = l.XM + (maxrows - 4 * decks) * l.XS / 2
  15030.         y = l.YM
  15031.         for i in range(4 * decks):
  15032.             s.foundations.append(_.Foundation_Class(x, y, _, suit = i / decks, max_move = _.FOUNDATION_MAX_MOVE))
  15033.             x = x + l.XS
  15034.         
  15035.         x = l.XM + (maxrows - rows) * l.XS / 2
  15036.         y = l.YM + l.YS
  15037.         for i in range(rows):
  15038.             s.rows.append(_.RowStack_Class(x, y, _, max_move = _.ROW_MAX_MOVE))
  15039.             x = x + l.XS
  15040.         
  15041.         x = _.width - l.XS
  15042.         y = _.height - l.YS - l.YBOTTOM
  15043.         s.talon = WasteTalonStack(x, y, _, max_rounds = max_rounds, num_deal = num_deal)
  15044.         l.createText(s.talon, 's')
  15045.         x = x - l.XS
  15046.         s.waste = WasteStack(x, y, _)
  15047.         s.waste.CARD_XOFFSET = -(l.XOFFSET)
  15048.         l.createText(s.waste, 's')
  15049.         l.defaultStackGroups()
  15050.  
  15051.     
  15052.     def startGame(_):
  15053.         for i in range(_.DEAL[0]):
  15054.             _.s.talon.dealRow(flip = 0, frames = 0)
  15055.         
  15056.         for i in range(_.DEAL[1] - 1):
  15057.             _.s.talon.dealRow(frames = 0)
  15058.         
  15059.         _.startDealSample()
  15060.         _.s.talon.dealRow()
  15061.         _.s.talon.dealCards()
  15062.  
  15063.     
  15064.     def fillStack(_, stack):
  15065.         if _.FILL_EMPTY_ROWS and stack in _.s.rows and not (stack.cards):
  15066.             old_state = _.enterState(_.S_FILL)
  15067.             if _.s.waste.cards:
  15068.                 _.s.waste.moveMove(1, stack)
  15069.             elif _.s.talon.canDealCards():
  15070.                 _.s.talon.dealCards()
  15071.                 _.s.waste.moveMove(1, stack)
  15072.             
  15073.             _.leaveState(old_state)
  15074.         
  15075.  
  15076.     
  15077.     def shallHighlightMatch(_, stack1, card1, stack2, card2):
  15078.         if not card1.suit == card2.suit and card1.rank + 1 == card2.rank:
  15079.             pass
  15080.         return card2.rank + 1 == card1.rank
  15081.  
  15082.  
  15083.  
  15084. class BusyAces(FortyThieves):
  15085.     DEAL = (0, 1)
  15086.     
  15087.     def createGame(_):
  15088.         FortyThieves.createGame(_, rows = 12)
  15089.  
  15090.  
  15091.  
  15092. class Limited(BusyAces):
  15093.     DEAL = (0, 3)
  15094.  
  15095.  
  15096. class Courtyard(BusyAces):
  15097.     ROW_MAX_MOVE = 999999
  15098.     FILL_EMPTY_ROWS = 1
  15099.  
  15100.  
  15101. class WaningMoon(FortyThieves):
  15102.     
  15103.     def createGame(_):
  15104.         FortyThieves.createGame(_, rows = 13)
  15105.  
  15106.  
  15107.  
  15108. class Lucas(WaningMoon):
  15109.     ROW_MAX_MOVE = 999999
  15110.  
  15111.  
  15112. class Deuces(FortyThieves):
  15113.     Foundation_Class = StackWrapper(SS_FoundationStack, mod = 13, base_rank = 1)
  15114.     RowStack_Class = StackWrapper(SS_RowStack, mod = 13)
  15115.     DEAL = (0, 1)
  15116.     
  15117.     def _shuffleHook(_, cards):
  15118.         return _._shuffleHookMoveToTop(cards, (lambda c: (c.rank == 1, c.suit)))
  15119.  
  15120.     
  15121.     def startGame(_):
  15122.         _.startDealSample()
  15123.         _.s.talon.dealRow(rows = _.s.foundations)
  15124.         FortyThieves.startGame(_)
  15125.  
  15126.  
  15127.  
  15128. class Corona(FortyThieves):
  15129.     FOUNDATION_MAX_MOVE = 0
  15130.     DEAL = (0, 3)
  15131.     FILL_EMPTY_ROWS = 1
  15132.     
  15133.     def createGame(_):
  15134.         FortyThieves.createGame(_, rows = 12)
  15135.  
  15136.  
  15137.  
  15138. class Quadrangle(Corona):
  15139.     Foundation_Class = StackWrapper(SS_FoundationStack, mod = 13, base_rank = NO_RANK)
  15140.     RowStack_Class = StackWrapper(SS_RowStack, mod = 13)
  15141.     
  15142.     def startGame(_):
  15143.         FortyThieves.startGame(_)
  15144.         _.s.talon.dealSingleBaseCard()
  15145.  
  15146.  
  15147.  
  15148. class FortyAndEight(FortyThieves):
  15149.     
  15150.     def createGame(_):
  15151.         FortyThieves.createGame(_, max_rounds = 2, rows = 8, XCARDS = 72, XOFFSET = 8)
  15152.  
  15153.  
  15154.  
  15155. class LittleForty(FortyThieves):
  15156.     RowStack_Class = Spider_SS_RowStack
  15157.     ROW_MAX_MOVE = 999999
  15158.     FILL_EMPTY_ROWS = 1
  15159.     
  15160.     def createGame(_):
  15161.         FortyThieves.createGame(_, max_rounds = 4, num_deal = 3, XOFFSET = 0)
  15162.  
  15163.  
  15164.  
  15165. class Streets(FortyThieves):
  15166.     RowStack_Class = AC_RowStack
  15167.     
  15168.     def shallHighlightMatch(_, stack1, card1, stack2, card2):
  15169.         if not card1.color != card2.color and card1.rank + 1 == card2.rank:
  15170.             pass
  15171.         return card2.rank + 1 == card1.rank
  15172.  
  15173.  
  15174.  
  15175. class Maria(Streets):
  15176.     
  15177.     def createGame(_):
  15178.         Streets.createGame(_, rows = 9)
  15179.  
  15180.  
  15181.  
  15182. class NumberTen(Streets):
  15183.     ROW_MAX_MOVE = 999999
  15184.     DEAL = (2, 2)
  15185.  
  15186.  
  15187. class RankAndFile(Streets):
  15188.     ROW_MAX_MOVE = 999999
  15189.     DEAL = (3, 1)
  15190.  
  15191.  
  15192. class TripleLine(Streets):
  15193.     GAME_VERSION = 2
  15194.     FOUNDATION_MAX_MOVE = 0
  15195.     ROW_MAX_MOVE = 999999
  15196.     DEAL = (0, 3)
  15197.     FILL_EMPTY_ROWS = 1
  15198.     
  15199.     def createGame(_):
  15200.         Streets.createGame(_, max_rounds = 2, rows = 12)
  15201.  
  15202.  
  15203.  
  15204. class RedAndBlack(Streets):
  15205.     Foundation_Class = AC_FoundationStack
  15206.     ROW_MAX_MOVE = 999999
  15207.     DEAL = (0, 1)
  15208.     
  15209.     def createGame(_):
  15210.         FortyThieves.createGame(_, rows = 8)
  15211.  
  15212.     
  15213.     def _shuffleHook(_, cards):
  15214.         return _._shuffleHookMoveToTop(cards, (lambda c: (c.rank == 0, c.suit)))
  15215.  
  15216.     
  15217.     def startGame(_):
  15218.         _.startDealSample()
  15219.         _.s.talon.dealRow(rows = _.s.foundations)
  15220.         Streets.startGame(_)
  15221.  
  15222.  
  15223.  
  15224. class Zebra(RedAndBlack):
  15225.     FOUNDATION_MAX_MOVE = 0
  15226.     ROW_MAX_MOVE = 1
  15227.     FILL_EMPTY_ROWS = 1
  15228.     
  15229.     def createGame(_):
  15230.         FortyThieves.createGame(_, max_rounds = 2, rows = 8, XOFFSET = 0)
  15231.  
  15232.  
  15233.  
  15234. class Indian_RowStack(SequenceRowStack):
  15235.     
  15236.     def _isSequence(_, cards):
  15237.         return isAnySuitButOwnSequence(cards, _.cap.mod, _.cap.dir)
  15238.  
  15239.  
  15240.  
  15241. class Indian(FortyThieves):
  15242.     RowStack_Class = Indian_RowStack
  15243.     DEAL = (1, 2)
  15244.     
  15245.     def createGame(_):
  15246.         FortyThieves.createGame(_, XCARDS = 74, XOFFSET = 8)
  15247.  
  15248.     
  15249.     def shallHighlightMatch(_, stack1, card1, stack2, card2):
  15250.         if not card1.suit != card2.suit and card1.rank + 1 == card2.rank:
  15251.             pass
  15252.         return card2.rank + 1 == card1.rank
  15253.  
  15254.  
  15255.  
  15256. class Midshipman(Indian):
  15257.     DEAL = (2, 2)
  15258.     
  15259.     def createGame(_):
  15260.         FortyThieves.createGame(_, rows = 9)
  15261.  
  15262.  
  15263.  
  15264. class NapoleonsExile(FortyThieves):
  15265.     RowStack_Class = RK_RowStack
  15266.     DEAL = (0, 4)
  15267.     
  15268.     def shallHighlightMatch(_, stack1, card1, stack2, card2):
  15269.         if not card1.rank + 1 == card2.rank:
  15270.             pass
  15271.         return card2.rank + 1 == card1.rank
  15272.  
  15273.  
  15274.  
  15275. class DoubleRail(NapoleonsExile):
  15276.     ROW_MAX_MOVE = 999999
  15277.     DEAL = (0, 1)
  15278.     
  15279.     def createGame(_):
  15280.         FortyThieves.createGame(_, rows = 5)
  15281.  
  15282.  
  15283.  
  15284. class SingleRail(DoubleRail):
  15285.     
  15286.     def createGame(_):
  15287.         FortyThieves.createGame(_, rows = 4, XCARDS = 48)
  15288.  
  15289.  
  15290. registerGame(GameInfo(13, FortyThieves, 'Forty Thieves', GI.GT_FORTY_THIEVES, 2, 0, altnames = ('Napoleon at St.Helena', 'Le Cadran')))
  15291. registerGame(GameInfo(80, BusyAces, 'Busy Aces', GI.GT_FORTY_THIEVES, 2, 0))
  15292. registerGame(GameInfo(228, Limited, 'Limited', GI.GT_FORTY_THIEVES, 2, 0))
  15293. registerGame(GameInfo(79, WaningMoon, 'Waning Moon', GI.GT_FORTY_THIEVES, 2, 0))
  15294. registerGame(GameInfo(125, Lucas, 'Lucas', GI.GT_FORTY_THIEVES, 2, 0))
  15295. registerGame(GameInfo(109, Deuces, 'Deuces', GI.GT_FORTY_THIEVES, 2, 0))
  15296. registerGame(GameInfo(196, Corona, 'Corona', GI.GT_FORTY_THIEVES, 2, 0))
  15297. registerGame(GameInfo(195, Quadrangle, 'Quadrangle', GI.GT_FORTY_THIEVES, 2, 0))
  15298. registerGame(GameInfo(110, Courtyard, 'Courtyard', GI.GT_FORTY_THIEVES, 2, 0))
  15299. registerGame(GameInfo(23, FortyAndEight, 'Forty and Eight', GI.GT_FORTY_THIEVES, 2, 1))
  15300. registerGame(GameInfo(115, LittleForty, 'Little Forty', GI.GT_FORTY_THIEVES, 2, 3))
  15301. registerGame(GameInfo(76, Streets, 'Streets', GI.GT_FORTY_THIEVES, 2, 0))
  15302. registerGame(GameInfo(73, Maria, 'Maria', GI.GT_FORTY_THIEVES, 2, 0))
  15303. registerGame(GameInfo(70, NumberTen, 'Number Ten', GI.GT_FORTY_THIEVES, 2, 0))
  15304. registerGame(GameInfo(71, RankAndFile, 'Rank and File', GI.GT_FORTY_THIEVES, 2, 0, altnames = 'Emperor'))
  15305. registerGame(GameInfo(126, RedAndBlack, 'Red and Black', GI.GT_FORTY_THIEVES, 2, 0))
  15306. registerGame(GameInfo(113, Zebra, 'Zebra', GI.GT_FORTY_THIEVES, 2, 1))
  15307. registerGame(GameInfo(69, Indian, 'Indian', GI.GT_FORTY_THIEVES, 2, 0))
  15308. registerGame(GameInfo(74, Midshipman, 'Midshipman', GI.GT_FORTY_THIEVES, 2, 0))
  15309. registerGame(GameInfo(131, DoubleRail, 'Double Rail', GI.GT_FORTY_THIEVES, 2, 0))
  15310. registerGame(GameInfo(199, SingleRail, 'Single Rail', GI.GT_FORTY_THIEVES, 1, 0))
  15311.  
  15312. class Diplomat(Game):
  15313.     Foundation_Class = SS_FoundationStack
  15314.     RowStack_Class = RK_RowStack
  15315.     Hint_Class = FortyThieves_Hint
  15316.     FOUNDATION_MAX_MOVE = 0
  15317.     ROW_MAX_MOVE = 1
  15318.     DEAL = (3, 1)
  15319.     FILL_EMPTY_ROWS = 0
  15320.     
  15321.     def createGame(_):
  15322.         (l, s) = (Layout(_), _.s)
  15323.         _.setSize(l.XM + 8 * l.XS, l.YM + 5 * l.YS)
  15324.         (x, y) = (l.XM, l.YM)
  15325.         for i in range(8):
  15326.             s.foundations.append(_.Foundation_Class(x, y, _, suit = i / 2, max_move = _.FOUNDATION_MAX_MOVE))
  15327.             x = x + l.XS
  15328.         
  15329.         (x, y) = (l.XM, y + l.YS)
  15330.         for i in range(8):
  15331.             s.rows.append(_.RowStack_Class(x, y, _, max_move = _.ROW_MAX_MOVE))
  15332.             x = x + l.XS
  15333.         
  15334.         (x, y) = (l.XM, _.height - l.YS)
  15335.         s.talon = WasteTalonStack(x, y, _, max_rounds = 1)
  15336.         l.createText(s.talon, 'nn')
  15337.         x = x + l.XS
  15338.         s.waste = WasteStack(x, y, _)
  15339.         l.createText(s.waste, 'nn')
  15340.         l.defaultStackGroups()
  15341.  
  15342.     
  15343.     def startGame(_):
  15344.         for i in range(_.DEAL[0]):
  15345.             _.s.talon.dealRow(frames = 0)
  15346.         
  15347.         _.startDealSample()
  15348.         for i in range(_.DEAL[1]):
  15349.             _.s.talon.dealRow()
  15350.         
  15351.         _.s.talon.dealCards()
  15352.  
  15353.     
  15354.     def fillStack(_, stack):
  15355.         if _.FILL_EMPTY_ROWS and stack in _.s.rows and not (stack.cards):
  15356.             old_state = _.enterState(_.S_FILL)
  15357.             if _.s.waste.cards:
  15358.                 _.s.waste.moveMove(1, stack)
  15359.             elif _.s.talon.canDealCards():
  15360.                 _.s.talon.dealCards()
  15361.                 _.s.waste.moveMove(1, stack)
  15362.             
  15363.             _.leaveState(old_state)
  15364.         
  15365.  
  15366.     
  15367.     def shallHighlightMatch(_, stack1, card1, stack2, card2):
  15368.         if not card1.rank + 1 == card2.rank:
  15369.             pass
  15370.         return card2.rank + 1 == card1.rank
  15371.  
  15372.  
  15373.  
  15374. class LadyPalk(Diplomat):
  15375.     ROW_MAX_MOVE = 999999
  15376.  
  15377.  
  15378. class Congress(Diplomat):
  15379.     DEAL = (0, 1)
  15380.     FILL_EMPTY_ROWS = 1
  15381.     
  15382.     def createGame(_):
  15383.         (l, s) = (Layout(_), _.s)
  15384.         _.setSize(l.XM + 7 * l.XS, l.YM + 4 * l.YS)
  15385.         for i in range(4):
  15386.             for j in range(2):
  15387.                 (x, y) = (l.XM + (4 + j) * l.XS, l.YM + i * l.YS)
  15388.                 s.foundations.append(_.Foundation_Class(x, y, _, suit = i, max_move = _.FOUNDATION_MAX_MOVE))
  15389.             
  15390.         
  15391.         for i in range(4):
  15392.             for j in range(2):
  15393.                 (x, y) = (l.XM + (3 + 3 * j) * l.XS, l.YM + i * l.YS)
  15394.                 stack = _.RowStack_Class(x, y, _, max_move = _.ROW_MAX_MOVE)
  15395.                 stack.CARD_YOFFSET = 0
  15396.                 s.rows.append(stack)
  15397.             
  15398.         
  15399.         (x, y) = (l.XM, l.YM)
  15400.         s.talon = WasteTalonStack(x, y, _, max_rounds = 1)
  15401.         l.createText(s.talon, 'ss')
  15402.         x = x + l.XS
  15403.         s.waste = WasteStack(x, y, _)
  15404.         l.createText(s.waste, 'ss')
  15405.         l.defaultStackGroups()
  15406.  
  15407.  
  15408. registerGame(GameInfo(149, Diplomat, 'Diplomat', GI.GT_FORTY_THIEVES, 2, 0))
  15409. registerGame(GameInfo(151, LadyPalk, 'Lady Palk', GI.GT_FORTY_THIEVES, 2, 0))
  15410. registerGame(GameInfo(150, Congress, 'Congress', GI.GT_FORTY_THIEVES, 2, 0))
  15411.  
  15412. class DoubleKlondike(Game):
  15413.     Layout_Method = Layout.harpLayout
  15414.     RowStack_Class = KingAC_RowStack
  15415.     Hint_Class = KlondikeType_Hint
  15416.     
  15417.     def createGame(_, max_rounds = -1, num_deal = 1, **layout):
  15418.         (l, s) = (Layout(_), _.s)
  15419.         kwdefault(layout, rows = 9, waste = 1, texts = 1, playcards = 19)
  15420.         apply(_.Layout_Method, (l,), layout)
  15421.         _.setSize(l.size[0], l.size[1])
  15422.         s.talon = WasteTalonStack(l.s.talon.x, l.s.talon.y, _, max_rounds = max_rounds, num_deal = num_deal)
  15423.         s.waste = WasteStack(l.s.waste.x, l.s.waste.y, _)
  15424.         for r in l.s.foundations:
  15425.             s.foundations.append(SS_FoundationStack(r.x, r.y, _, suit = r.suit))
  15426.         
  15427.         for r in l.s.rows:
  15428.             s.rows.append(_.RowStack_Class(r.x, r.y, _))
  15429.         
  15430.         l.defaultAll()
  15431.         if max_rounds > 1:
  15432.             if not __debug__ and s.talon.texts.rounds is None:
  15433.                 raise AssertionError
  15434.             0
  15435.             (tx, ty, ta, tf) = l.getTextAttr(s.talon, 'nn')
  15436.             s.talon.texts.rounds = MfxCanvasText(_.canvas, tx, ty, anchor = ta)
  15437.         
  15438.         return l
  15439.  
  15440.     
  15441.     def startGame(_):
  15442.         for i in range(len(_.s.rows)):
  15443.             _.s.talon.dealRow(rows = _.s.rows[i + 1:], flip = 0, frames = 0)
  15444.         
  15445.         _.startDealSample()
  15446.         _.s.talon.dealRow()
  15447.         _.s.talon.dealCards()
  15448.  
  15449.     
  15450.     def shallHighlightMatch(_, stack1, card1, stack2, card2):
  15451.         if not card1.color != card2.color and card1.rank + 1 == card2.rank:
  15452.             pass
  15453.         return card2.rank + 1 == card1.rank
  15454.  
  15455.  
  15456.  
  15457. class DoubleKlondikeByThrees(DoubleKlondike):
  15458.     
  15459.     def createGame(_):
  15460.         DoubleKlondike.createGame(_, num_deal = 3)
  15461.  
  15462.  
  15463.  
  15464. class Gargantua(DoubleKlondike):
  15465.     
  15466.     def createGame(_):
  15467.         DoubleKlondike.createGame(_, max_rounds = 2)
  15468.  
  15469.  
  15470.  
  15471. class BigHarp(DoubleKlondike):
  15472.     RowStack_Class = AC_RowStack
  15473.     
  15474.     def createGame(_):
  15475.         DoubleKlondike.createGame(_, max_rounds = 1, rows = 10)
  15476.  
  15477.     
  15478.     def startGame(_):
  15479.         for i in range(len(_.s.rows)):
  15480.             _.s.talon.dealRow(rows = _.s.rows[:i], flip = 0, frames = 0)
  15481.         
  15482.         _.startDealSample()
  15483.         _.s.talon.dealRow()
  15484.         _.s.talon.dealCards()
  15485.  
  15486.  
  15487.  
  15488. class Steps(DoubleKlondike):
  15489.     RowStack_Class = AC_RowStack
  15490.     
  15491.     def createGame(_):
  15492.         DoubleKlondike.createGame(_, max_rounds = 1, rows = 7)
  15493.  
  15494.  
  15495. registerGame(GameInfo(21, DoubleKlondike, 'Double Klondike', GI.GT_KLONDIKE, 2, -1))
  15496. registerGame(GameInfo(28, DoubleKlondikeByThrees, 'Double Klondike by Threes', GI.GT_KLONDIKE, 2, -1))
  15497. registerGame(GameInfo(25, Gargantua, 'Gargantua', GI.GT_KLONDIKE, 2, 1))
  15498. registerGame(GameInfo(15, BigHarp, 'Big Harp', GI.GT_KLONDIKE, 2, 0))
  15499. registerGame(GameInfo(51, Steps, 'Steps', GI.GT_KLONDIKE, 2, 0))
  15500.  
  15501. class PictureGallery_Hint(AbstractHint):
  15502.     
  15503.     def computeHints(_):
  15504.         game = _.game
  15505.         for r in game.sg.dropstacks:
  15506.             (t, n) = r.canDropCards(game.s.foundations)
  15507.         
  15508.         if not (_.hints):
  15509.             for r in game.sg.dropstacks:
  15510.                 pile = r.getPile()
  15511.                 for t in game.s.tableaux:
  15512.                     pass
  15513.                 
  15514.             
  15515.         
  15516.         if not (_.hints):
  15517.             for r in game.s.tableaux:
  15518.                 pile = r.getPile()
  15519.                 rr = _.ClonedStack(r, stackcards = r.cards[:-1])
  15520.                 if rr.acceptsCards(None, pile):
  15521.                     continue
  15522.                 
  15523.                 for t in game.s.rows:
  15524.                     pass
  15525.                 
  15526.             
  15527.         
  15528.         if not (_.hints):
  15529.             for r in game.s.rows:
  15530.                 pile = r.getPile()
  15531.                 base_score = 60000
  15532.                 for t in game.s.rows:
  15533.                     pass
  15534.                 
  15535.             
  15536.         
  15537.         if _.level >= 2:
  15538.             if game.canDealCards():
  15539.                 _.addHint(_.SCORE_DEAL, 0, game.s.talon, None)
  15540.             
  15541.         
  15542.  
  15543.  
  15544.  
  15545. class PictureGallery_Foundation(RK_FoundationStack):
  15546.     
  15547.     def __init__(_, x, y, game):
  15548.         RK_FoundationStack.__init__(_, x, y, game, base_rank = ACE, dir = 0, max_move = 0, max_cards = 8)
  15549.         _.CARD_YOFFSET = min(30, _.game.app.images.CARD_YOFFSET + 10)
  15550.  
  15551.     
  15552.     def getBottomImage(_):
  15553.         return _.game.app.images.getLetter(ACE)
  15554.  
  15555.  
  15556.  
  15557. class PictureGallery_TableauStack(SS_RowStack):
  15558.     
  15559.     def __init__(_, x, y, game, base_rank, yoffset):
  15560.         SS_RowStack.__init__(_, x, y, game, base_rank = base_rank, dir = 3, max_accept = 1)
  15561.         _.CARD_YOFFSET = yoffset
  15562.  
  15563.     
  15564.     def acceptsCards(_, from_stack, cards):
  15565.         if not SS_RowStack.acceptsCards(_, from_stack, cards):
  15566.             return 0
  15567.         
  15568.         if _.cards and _.cards[0].rank != _.cap.base_rank:
  15569.             return 0
  15570.         
  15571.         return 1
  15572.  
  15573.     
  15574.     def getBottomImage(_):
  15575.         return _.game.app.images.getLetter(_.cap.base_rank)
  15576.  
  15577.  
  15578.  
  15579. class PictureGallery_RowStack(BasicRowStack):
  15580.     
  15581.     def acceptsCards(_, from_stack, cards):
  15582.         if not BasicRowStack.acceptsCards(_, from_stack, cards):
  15583.             return 0
  15584.         
  15585.         if _.cards or _.game.s.talon.cards:
  15586.             return 0
  15587.         
  15588.         return 1
  15589.  
  15590.     
  15591.     def getBottomImage(_):
  15592.         return _.game.app.images.getTalonBottom()
  15593.  
  15594.  
  15595.  
  15596. class PictureGallery(Game):
  15597.     Hint_Class = PictureGallery_Hint
  15598.     
  15599.     def createGame(_):
  15600.         (l, s) = (Layout(_), _.s)
  15601.         TABLEAU_YOFFSET = min(9, max(3, l.YOFFSET / 3))
  15602.         th = l.YS + 3 * TABLEAU_YOFFSET
  15603.         h = (10 - 1) * l.YOFFSET + l.CH * 2 / 3
  15604.         _.setSize(10 * l.XS + l.XM, l.YM + 3 * th + l.YM + h)
  15605.         s.addattr(tableaux = [])
  15606.         x = l.XM + 8 * l.XS + l.XS / 2
  15607.         y = l.YM + l.CH / 2
  15608.         s.foundations.append(PictureGallery_Foundation(x, y, _))
  15609.         y = l.YM
  15610.         for i in (3, 2, 1):
  15611.             x = l.XM
  15612.             for j in range(8):
  15613.                 s.tableaux.append(PictureGallery_TableauStack(x, y, _, i, yoffset = TABLEAU_YOFFSET))
  15614.                 x = x + l.XS
  15615.             
  15616.             y = y + th
  15617.         
  15618.         (x, y) = (l.XM, y + l.YM)
  15619.         for i in range(8):
  15620.             s.rows.append(PictureGallery_RowStack(x, y, _, max_accept = 1))
  15621.             x = x + l.XS
  15622.         
  15623.         x = l.XM + 8 * l.XS + l.XS / 2
  15624.         y = _.height - l.YS
  15625.         s.talon = DealRowTalonStack(x, y, _)
  15626.         l.createText(s.talon, 'se')
  15627.         _.setRegion(s.foundations, (x - l.CW / 2, -999, 999999, y - l.CH))
  15628.         _.sg.openstacks = s.foundations + s.tableaux + s.rows
  15629.         _.sg.talonstacks = [
  15630.             s.talon]
  15631.         _.sg.dropstacks = s.tableaux + s.rows
  15632.  
  15633.     
  15634.     def startGame(_):
  15635.         _.s.talon.dealRow(rows = _.s.tableaux, frames = 0)
  15636.         _.startDealSample()
  15637.         _.s.talon.dealRow()
  15638.  
  15639.     
  15640.     def isGameWon(_):
  15641.         if len(_.s.foundations[0].cards) != 8:
  15642.             return 0
  15643.         
  15644.         for stack in _.s.tableaux:
  15645.             pass
  15646.         
  15647.         return 1
  15648.  
  15649.     
  15650.     def fillStack(_, stack):
  15651.         if _.s.talon.cards:
  15652.             if stack in _.s.rows and len(stack.cards) == 0:
  15653.                 _.s.talon.dealRow(rows = [
  15654.                     stack])
  15655.             
  15656.         
  15657.  
  15658.     
  15659.     def shallHighlightMatch(_, stack1, card1, stack2, card2):
  15660.         if card1.rank == ACE or card2.rank == ACE:
  15661.             return 0
  15662.         
  15663.         if not card1.suit == card2.suit and card1.rank + 3 == card2.rank:
  15664.             pass
  15665.         return card2.rank + 3 == card1.rank
  15666.  
  15667.     
  15668.     def getHighlightPilesStacks(_):
  15669.         return ()
  15670.  
  15671.  
  15672. registerGame(GameInfo(7, PictureGallery, 'Picture Gallery', GI.GT_2DECK_TYPE, 2, 0))
  15673.  
  15674. class Braid_Hint(DefaultHint):
  15675.     pass
  15676.  
  15677.  
  15678. class Braid_Foundation(AbstractFoundationStack):
  15679.     
  15680.     def __init__(_, x, y, game, suit, **cap):
  15681.         kwdefault(cap, mod = 13, dir = 0, base_rank = NO_RANK, max_move = 0)
  15682.         apply(AbstractFoundationStack.__init__, (_, x, y, game, suit), cap)
  15683.  
  15684.     
  15685.     def acceptsCards(_, from_stack, cards):
  15686.         if not AbstractFoundationStack.acceptsCards(_, from_stack, cards):
  15687.             return 0
  15688.         
  15689.         if not (_.cards):
  15690.             return 1
  15691.         
  15692.         stack_dir = _.game.getFoundationDir()
  15693.         if stack_dir == 0:
  15694.             card_dir = _.getRankDir(cards = (_.cards[-1], cards[0]))
  15695.             return card_dir in (1, -1)
  15696.         else:
  15697.             return (_.cards[-1].rank + stack_dir) % _.cap.mod == cards[0].rank
  15698.  
  15699.  
  15700.  
  15701. class Braid_BraidStack(OpenStack):
  15702.     
  15703.     def __init__(_, x, y, game, sine = 0):
  15704.         OpenStack.__init__(_, x, y, game)
  15705.         _.CARD_YOFFSET = _.game.app.images.CARD_YOFFSET
  15706.         CW = _.game.app.images.CARDW
  15707.  
  15708.  
  15709.  
  15710. class Braid_RowStack(ReserveStack):
  15711.     
  15712.     def fillStack(_):
  15713.         if not (_.cards) and _.game.s.braid.cards:
  15714.             _.game.moveMove(1, _.game.s.braid, _)
  15715.         
  15716.  
  15717.     
  15718.     def getBottomImage(_):
  15719.         return _.game.app.images.getBraidBottom()
  15720.  
  15721.  
  15722.  
  15723. class Braid_ReserveStack(ReserveStack):
  15724.     
  15725.     def acceptsCards(_, from_stack, cards):
  15726.         if from_stack is _.game.s.braid or from_stack in _.game.s.rows:
  15727.             return 0
  15728.         
  15729.         return ReserveStack.acceptsCards(_, from_stack, cards)
  15730.  
  15731.     
  15732.     def getBottomImage(_):
  15733.         return _.game.app.images.getTalonBottom()
  15734.  
  15735.  
  15736.  
  15737. class Braid(Game):
  15738.     Hint_Class = Braid_Hint
  15739.     BRAID_CARDS = 20
  15740.     RANKS = RANKS
  15741.     
  15742.     def createGame(_):
  15743.         (l, s) = (Layout(_), _.s)
  15744.         h = max(4 * l.YS + 30, l.YS + (_.BRAID_CARDS - 1) * l.YOFFSET)
  15745.         _.setSize(10 * l.XS + l.XM, l.YM + h)
  15746.         _.base_card = None
  15747.         s.addattr(braid = None)
  15748.         (x, y) = (l.XM, l.YM)
  15749.         for i in range(2):
  15750.             s.rows.append(Braid_RowStack(x + 0.5 * l.XS, y, _))
  15751.             s.rows.append(Braid_RowStack(x + 4.5 * l.XS, y, _))
  15752.             y = y + 3 * l.YS
  15753.         
  15754.         y = l.YM + l.YS
  15755.         for i in range(2):
  15756.             s.rows.append(Braid_ReserveStack(x, y, _))
  15757.             s.rows.append(Braid_ReserveStack(x + l.XS, y, _))
  15758.             s.rows.append(Braid_ReserveStack(x, y + l.YS, _))
  15759.             s.rows.append(Braid_ReserveStack(x + l.XS, y + l.YS, _))
  15760.             x = x + 4 * l.XS
  15761.         
  15762.         (x, y) = (l.XM + l.XS * 5 / 2, l.YM)
  15763.         s.braid = Braid_BraidStack(x, y, _)
  15764.         (x, y) = (l.XM + 7 * l.XS, l.YM + l.YS * 3 / 2)
  15765.         s.talon = WasteTalonStack(x, y, _, max_rounds = 3)
  15766.         l.createText(s.talon, 'ss')
  15767.         s.talon.texts.rounds = MfxCanvasText(_.canvas, x + l.CW / 2, y - l.YM, anchor = 's')
  15768.         x = x - l.XS
  15769.         s.waste = WasteStack(x, y, _)
  15770.         l.createText(s.waste, 'ss')
  15771.         x = l.XM + 8 * l.XS
  15772.         y = l.YM
  15773.         for i in range(4):
  15774.             s.foundations.append(Braid_Foundation(x, y, _, i))
  15775.             s.foundations.append(Braid_Foundation(x + l.XS, y, _, i))
  15776.             y = y + l.YS
  15777.         
  15778.         _.texts.info = MfxCanvasText(_.canvas, x + l.CW + l.XM / 2, y, anchor = 'n', font = getFont('canvas_card', cardw = l.CW))
  15779.         _.sg.talonstacks = [
  15780.             s.talon] + [
  15781.             s.waste]
  15782.         _.sg.openstacks = s.foundations + s.rows
  15783.         _.sg.dropstacks = [
  15784.             s.braid] + s.rows + [
  15785.             s.waste]
  15786.  
  15787.     
  15788.     def _shuffleHook(_, cards):
  15789.         n = m = -1 - _.BRAID_CARDS - len(_.s.rows)
  15790.         while cards[n].suit >= len(_.gameinfo.suits):
  15791.             n = n - 1
  15792.         (cards[n], cards[m]) = (cards[m], cards[n])
  15793.         return cards
  15794.  
  15795.     
  15796.     def startGame(_):
  15797.         _.base_card = None
  15798.         _.updateText()
  15799.         _.startDealSample()
  15800.         for i in range(_.BRAID_CARDS):
  15801.             _.s.talon.dealRow(rows = [
  15802.                 _.s.braid], frames = 4)
  15803.         
  15804.         _.s.talon.dealRow(frames = 4)
  15805.         _.base_card = _.s.talon.cards[-1]
  15806.         to_stack = _.s.foundations[2 * _.base_card.suit]
  15807.         _.flipMove(_.s.talon)
  15808.         _.moveMove(1, _.s.talon, to_stack)
  15809.         _.updateText()
  15810.         for s in _.s.foundations:
  15811.             s.cap.base_rank = _.base_card.rank
  15812.         
  15813.         _.s.talon.dealCards()
  15814.  
  15815.     
  15816.     def shallHighlightMatch(_, stack1, card1, stack2, card2):
  15817.         if not card1.suit == card2.suit and (card1.rank + 1) % 13 == card2.rank:
  15818.             pass
  15819.         return (card2.rank + 1) % 13 == card1.rank
  15820.  
  15821.     
  15822.     def getHighlightPilesStacks(_):
  15823.         return ()
  15824.  
  15825.     
  15826.     def _restoreGameHook(_, game):
  15827.         _.base_card = _.cards[game.loadinfo.base_card_id]
  15828.         for s in _.s.foundations:
  15829.             s.cap.base_rank = _.base_card.rank
  15830.         
  15831.  
  15832.     
  15833.     def _loadGameHook(_, p):
  15834.         _.loadinfo.addattr(base_card_id = None)
  15835.         _.loadinfo.base_card_id = p.load()
  15836.  
  15837.     
  15838.     def _saveGameHook(_, p):
  15839.         p.dump(_.base_card.id)
  15840.  
  15841.     
  15842.     def updateText(_):
  15843.         if _.preview > 1 or not (_.texts.info):
  15844.             return None
  15845.         
  15846.         if not (_.base_card):
  15847.             t = ''
  15848.         else:
  15849.             t = _.RANKS[_.base_card.rank]
  15850.             dir = _.getFoundationDir()
  15851.             if dir == 1:
  15852.                 t = t + ' Ascending'
  15853.             elif dir == -1:
  15854.                 t = t + ' Descending'
  15855.             
  15856.         _.texts.info.config(text = t)
  15857.  
  15858.  
  15859.  
  15860. class LongBraid(Braid):
  15861.     BRAID_CARDS = 24
  15862.  
  15863. registerGame(GameInfo(12, Braid, 'Braid', GI.GT_NAPOLEON, 2, 2))
  15864. registerGame(GameInfo(175, LongBraid, 'Long Braid', GI.GT_NAPOLEON, 2, 2))
  15865.  
  15866. class Spider_Hint(SpiderType_Hint):
  15867.     BONUS_SAME_SUIT_MOVE = 400
  15868.     
  15869.     def _preferHighRankMoves(_):
  15870.         return 1
  15871.  
  15872.     
  15873.     def shallMovePile(_, r, t, pile, rpile):
  15874.         if not SpiderType_Hint.shallMovePile(_, r, t, pile, rpile):
  15875.             return 0
  15876.         
  15877.         rr = _.ClonedStack(r, stackcards = rpile)
  15878.         if rr.acceptsCards(t, pile):
  15879.             if len(t.cards) == 0:
  15880.                 return 1
  15881.             
  15882.             if pile[0].suit == t.cards[-1].suit:
  15883.                 if len(rpile) == 0 or pile[0].suit != rpile[-1].suit:
  15884.                     return 1
  15885.                 
  15886.             
  15887.             if _.level <= 1 and len(rpile) == 0:
  15888.                 return 1
  15889.             
  15890.             return 0
  15891.         
  15892.         return 1
  15893.  
  15894.  
  15895.  
  15896. class Spider_SS_Foundation(AbstractFoundationStack):
  15897.     
  15898.     def __init__(_, x, y, game, suit = ANY_SUIT, **cap):
  15899.         kwdefault(cap, dir = -1, base_rank = KING, min_accept = 13, max_accept = 13, max_move = 0)
  15900.         apply(AbstractFoundationStack.__init__, (_, x, y, game, suit), cap)
  15901.  
  15902.     
  15903.     def acceptsCards(_, from_stack, cards):
  15904.         if not AbstractFoundationStack.acceptsCards(_, from_stack, cards):
  15905.             return 0
  15906.         
  15907.         return isSameSuitSequence(cards, _.cap.mod, _.cap.dir)
  15908.  
  15909.  
  15910.  
  15911. class Spider_AC_Foundation(Spider_SS_Foundation):
  15912.     
  15913.     def acceptsCards(_, from_stack, cards):
  15914.         if not AbstractFoundationStack.acceptsCards(_, from_stack, cards):
  15915.             return 0
  15916.         
  15917.         return isAlternateColorSequence(cards, _.cap.mod, _.cap.dir)
  15918.  
  15919.  
  15920.  
  15921. class Spider_RowStack(Spider_SS_RowStack):
  15922.     
  15923.     def canDropCards(_, stacks):
  15924.         if len(_.cards) < 13:
  15925.             return (None, 0)
  15926.         
  15927.         cards = _.cards[-13:]
  15928.         for s in stacks:
  15929.             pass
  15930.         
  15931.         return (None, 0)
  15932.  
  15933.  
  15934.  
  15935. class RelaxedSpider(Game):
  15936.     Layout_Method = Layout.klondikeLayout
  15937.     Talon_Class = DealRowTalonStack
  15938.     Foundation_Class = Spider_SS_Foundation
  15939.     RowStack_Class = Spider_RowStack
  15940.     Hint_Class = Spider_Hint
  15941.     
  15942.     def createGame(_, **layout):
  15943.         (l, s) = (Layout(_), _.s)
  15944.         kwdefault(layout, rows = 10, waste = 0, texts = 1, playcards = 23)
  15945.         apply(_.Layout_Method, (l,), layout)
  15946.         _.setSize(l.size[0], l.size[1])
  15947.         s.talon = _.Talon_Class(l.s.talon.x, l.s.talon.y, _)
  15948.         if l.s.waste:
  15949.             s.waste = WasteStack(l.s.waste.x, l.s.waste.y, _)
  15950.         
  15951.         for r in l.s.foundations:
  15952.             s.foundations.append(_.Foundation_Class(r.x, r.y, _, suit = ANY_SUIT))
  15953.         
  15954.         for r in l.s.rows:
  15955.             s.rows.append(_.RowStack_Class(r.x, r.y, _))
  15956.         
  15957.         l.defaultAll()
  15958.  
  15959.     
  15960.     def startGame(_):
  15961.         for i in range(4):
  15962.             _.s.talon.dealRow(flip = 0, frames = 0)
  15963.         
  15964.         _.startDealSample()
  15965.         r = _.s.rows
  15966.         rows = (r[0], r[3], r[6], r[9])
  15967.         _.s.talon.dealRow(rows = rows, flip = 0)
  15968.         _.s.talon.dealRow()
  15969.  
  15970.     
  15971.     def shallHighlightMatch(_, stack1, card1, stack2, card2):
  15972.         if not (card1.rank + 1) % stack1.cap.mod == card2.rank:
  15973.             pass
  15974.         return (card2.rank + 1) % stack1.cap.mod == card1.rank
  15975.  
  15976.  
  15977.  
  15978. class Spider(RelaxedSpider):
  15979.     
  15980.     def canDealCards(_):
  15981.         if not RelaxedSpider.canDealCards(_):
  15982.             return 0
  15983.         
  15984.         for r in _.s.rows:
  15985.             pass
  15986.         
  15987.         return 1
  15988.  
  15989.  
  15990.  
  15991. class BlackWidow_RowStack(RK_RowStack, Spider_RowStack):
  15992.     
  15993.     def canDropCards(_, stacks):
  15994.         return Spider_RowStack.canDropCards(_, stacks)
  15995.  
  15996.  
  15997.  
  15998. class BlackWidow(Spider):
  15999.     RowStack_Class = BlackWidow_RowStack
  16000.  
  16001.  
  16002. class GroundForADivorce_Talon(TalonStack):
  16003.     
  16004.     def dealCards(_, sound = 1):
  16005.         if _.cards:
  16006.             rows = filter((lambda r: r.cards), _.game.s.rows)
  16007.             if not rows:
  16008.                 rows = _.game.s.rows[:1]
  16009.             
  16010.             return _.dealRowAvail(rows = rows, sound = sound)
  16011.         
  16012.         return 0
  16013.  
  16014.  
  16015.  
  16016. class GroundForADivorce(RelaxedSpider):
  16017.     Layout_Method = Layout.harpLayout
  16018.     Talon_Class = GroundForADivorce_Talon
  16019.     Foundation_Class = StackWrapper(Spider_SS_Foundation, base_rank = ANY_RANK, mod = 13)
  16020.     RowStack_Class = StackWrapper(Spider_RowStack, mod = 13)
  16021.     
  16022.     def createGame(_):
  16023.         Spider.createGame(_, playcards = 22)
  16024.  
  16025.     
  16026.     def startGame(_):
  16027.         for i in range(4):
  16028.             _.s.talon.dealRow(frames = 0)
  16029.         
  16030.         _.startDealSample()
  16031.         _.s.talon.dealRow()
  16032.  
  16033.  
  16034.  
  16035. class GrandmothersGame(RelaxedSpider):
  16036.     Layout_Method = Layout.harpLayout
  16037.     
  16038.     def createGame(_):
  16039.         Spider.createGame(_, playcards = 22)
  16040.  
  16041.     
  16042.     def startGame(_):
  16043.         for i in range(5):
  16044.             _.s.talon.dealRow(frames = 0)
  16045.         
  16046.         _.startDealSample()
  16047.         _.s.talon.dealRow()
  16048.  
  16049.  
  16050.  
  16051. class Spiderette(Spider):
  16052.     
  16053.     def createGame(_):
  16054.         Spider.createGame(_, rows = 7, playcards = 20)
  16055.  
  16056.     
  16057.     def startGame(_):
  16058.         for i in range(1, len(_.s.rows)):
  16059.             _.s.talon.dealRow(rows = _.s.rows[i:], flip = 0, frames = 0)
  16060.         
  16061.         _.startDealSample()
  16062.         _.s.talon.dealRow()
  16063.  
  16064.  
  16065.  
  16066. class BabySpiderette(Spiderette):
  16067.     RowStack_Class = BlackWidow_RowStack
  16068.  
  16069.  
  16070. class WillOTheWisp(Spiderette):
  16071.     
  16072.     def startGame(_):
  16073.         for i in range(2):
  16074.             _.s.talon.dealRow(flip = 0, frames = 0)
  16075.         
  16076.         _.startDealSample()
  16077.         _.s.talon.dealRow()
  16078.  
  16079.  
  16080.  
  16081. class SimpleSimon(Spider):
  16082.     Talon_Class = InitialDealTalonStack
  16083.     
  16084.     def createGame(_):
  16085.         Spider.createGame(_, rows = 10, texts = 0)
  16086.  
  16087.     
  16088.     def startGame(_):
  16089.         for l in (9, 8, 7, 6, 5, 4, 3):
  16090.             _.s.talon.dealRow(rows = _.s.rows[:l], frames = 0)
  16091.         
  16092.         _.startDealSample()
  16093.         _.s.talon.dealRow()
  16094.  
  16095.  
  16096.  
  16097. class Rachel(RelaxedSpider):
  16098.     Talon_Class = StackWrapper(WasteTalonStack, max_rounds = 1)
  16099.     RowStack_Class = RK_RowStack
  16100.     
  16101.     def createGame(_):
  16102.         Spider.createGame(_, waste = 1, rows = 6, texts = 1)
  16103.  
  16104.     
  16105.     def startGame(_):
  16106.         _.startDealSample()
  16107.         _.s.talon.dealRow()
  16108.         _.s.talon.dealCards()
  16109.  
  16110.  
  16111.  
  16112. class Scorpion_RowStack(Yukon_SS_RowStack, Spider_RowStack):
  16113.     canDropCards = Spider_RowStack.canDropCards
  16114.  
  16115.  
  16116. class Scorpion(RelaxedSpider):
  16117.     RowStack_Class = StackWrapper(Scorpion_RowStack, base_rank = KING)
  16118.     
  16119.     def createGame(_):
  16120.         Spider.createGame(_, rows = 7, playcards = 20)
  16121.  
  16122.     
  16123.     def startGame(_):
  16124.         for i in (4, 4, 4, 0, 0, 0):
  16125.             _.s.talon.dealRow(rows = _.s.rows[:i], flip = 0, frames = 0)
  16126.             _.s.talon.dealRow(rows = _.s.rows[i:], flip = 1, frames = 0)
  16127.         
  16128.         _.startDealSample()
  16129.         _.s.talon.dealRow()
  16130.  
  16131.     
  16132.     def shallHighlightMatch(_, stack1, card1, stack2, card2):
  16133.         if not card1.suit == card2.suit and card1.rank + 1 == card2.rank:
  16134.             pass
  16135.         return card2.rank + 1 == card1.rank
  16136.  
  16137.     
  16138.     def getHighlightPilesStacks(_):
  16139.         return ()
  16140.  
  16141.  
  16142.  
  16143. class Wasp(Scorpion):
  16144.     RowStack_Class = Scorpion_RowStack
  16145.     
  16146.     def startGame(_):
  16147.         for i in (3, 3, 3, 0, 0, 0):
  16148.             _.s.talon.dealRow(rows = _.s.rows[:i], flip = 0, frames = 0)
  16149.             _.s.talon.dealRow(rows = _.s.rows[i:], flip = 1, frames = 0)
  16150.         
  16151.         _.startDealSample()
  16152.         _.s.talon.dealRow()
  16153.  
  16154.  
  16155.  
  16156. class RougeEtNoir_RowStack(KingAC_RowStack):
  16157.     
  16158.     def canDropCards(_, stacks):
  16159.         if not (_.cards):
  16160.             return (None, 0)
  16161.         
  16162.         for s in stacks:
  16163.             for cards in (_.cards[-1:], _.cards[-13:]):
  16164.                 pass
  16165.             
  16166.         
  16167.         return (None, 0)
  16168.  
  16169.  
  16170.  
  16171. class RougeEtNoir(Game):
  16172.     Layout_Method = Layout.klondikeLayout
  16173.     Talon_Class = DealRowTalonStack
  16174.     RowStack_Class = RougeEtNoir_RowStack
  16175.     
  16176.     def createGame(_, **layout):
  16177.         (l, s) = (Layout(_), _.s)
  16178.         kwdefault(layout, rows = 10, waste = 0, texts = 1, playcards = 23)
  16179.         apply(_.Layout_Method, (l,), layout)
  16180.         _.setSize(l.size[0], l.size[1])
  16181.         s.talon = _.Talon_Class(l.s.talon.x, l.s.talon.y, _)
  16182.         if l.s.waste:
  16183.             s.waste = WasteStack(l.s.waste.x, l.s.waste.y, _)
  16184.         
  16185.         for i in range(4):
  16186.             r = l.s.foundations[i]
  16187.             s.foundations.append(AC_FoundationStack(r.x, r.y, _, suit = i, max_move = 0))
  16188.         
  16189.         for i in range(4):
  16190.             r = l.s.foundations[i + 4]
  16191.             s.foundations.append(Spider_AC_Foundation(r.x, r.y, _))
  16192.         
  16193.         for r in l.s.rows:
  16194.             s.rows.append(_.RowStack_Class(r.x, r.y, _))
  16195.         
  16196.         l.defaultAll()
  16197.         return l
  16198.  
  16199.     
  16200.     def startGame(_, flip = 0, reverse = 1):
  16201.         for i in range(3, len(_.s.rows)):
  16202.             _.s.talon.dealRow(rows = _.s.rows[:-i], flip = flip, frames = 0, reverse = reverse)
  16203.         
  16204.         _.startDealSample()
  16205.         _.s.talon.dealRow(rows = _.s.rows[:-1], reverse = reverse)
  16206.  
  16207.  
  16208. registerGame(GameInfo(10, RelaxedSpider, 'Relaxed Spider', GI.GT_SPIDER | GI.GT_RELAXED, 2, 0))
  16209. registerGame(GameInfo(11, Spider, 'Spider', GI.GT_SPIDER, 2, 0))
  16210. registerGame(GameInfo(49, BlackWidow, 'Black Widow', GI.GT_SPIDER, 2, 0))
  16211. registerGame(GameInfo(14, GroundForADivorce, 'Ground for a Divorce', GI.GT_SPIDER, 2, 0))
  16212. registerGame(GameInfo(114, GrandmothersGame, "Grandmother's Game", GI.GT_SPIDER, 2, 0))
  16213. registerGame(GameInfo(24, Spiderette, 'Spiderette', GI.GT_SPIDER, 1, 0))
  16214. registerGame(GameInfo(47, BabySpiderette, 'Baby Spiderette', GI.GT_SPIDER, 1, 0))
  16215. registerGame(GameInfo(48, WillOTheWisp, "Will o' the Wisp", GI.GT_SPIDER, 1, 0))
  16216. registerGame(GameInfo(50, SimpleSimon, 'Simple Simon', GI.GT_SPIDER, 1, 0))
  16217. registerGame(GameInfo(29, Scorpion, 'Scorpion', GI.GT_SPIDER, 1, 0))
  16218. registerGame(GameInfo(185, Wasp, 'Wasp', GI.GT_SPIDER, 1, 0))
  16219. registerGame(GameInfo(220, RougeEtNoir, 'Rouge et Noir', GI.GT_GYPSY, 2, 0))
  16220.  
  16221. class FreeCell_RowStack(AC_RowStack):
  16222.     
  16223.     def _getMaxMove(_, to_stack_ncards):
  16224.         max_move = getNumberOfFreeStacks(_.game.s.reserves) + 1
  16225.         n = getNumberOfFreeStacks(_.game.s.rows)
  16226.         if to_stack_ncards == 0:
  16227.             n = n - 1
  16228.         
  16229.         while n > 0 and max_move < 1000:
  16230.             max_move = max_move * 2
  16231.             n = n - 1
  16232.         return max_move
  16233.  
  16234.     
  16235.     def canMoveCards(_, cards):
  16236.         max_move = _._getMaxMove(1)
  16237.         if len(cards) <= max_move:
  16238.             pass
  16239.         return AC_RowStack.canMoveCards(_, cards)
  16240.  
  16241.     
  16242.     def acceptsCards(_, from_stack, cards):
  16243.         max_move = _._getMaxMove(len(_.cards))
  16244.         if len(cards) <= max_move:
  16245.             pass
  16246.         return AC_RowStack.acceptsCards(_, from_stack, cards)
  16247.  
  16248.  
  16249.  
  16250. class FreeCell(Game):
  16251.     Layout_Method = Layout.freeCellLayout
  16252.     Talon_Class = InitialDealTalonStack
  16253.     Foundation_Class = SS_FoundationStack
  16254.     RowStack_Class = FreeCell_RowStack
  16255.     Hint_Class = FreeCellType_Hint
  16256.     
  16257.     def createGame(_, **layout):
  16258.         (l, s) = (Layout(_), _.s)
  16259.         kwdefault(layout, rows = 8, reserves = 4, texts = 0)
  16260.         apply(_.Layout_Method, (l,), layout)
  16261.         _.setSize(l.size[0], l.size[1])
  16262.         s.talon = _.Talon_Class(l.s.talon.x, l.s.talon.y, _)
  16263.         for r in l.s.foundations:
  16264.             s.foundations.append(_.Foundation_Class(r.x, r.y, _, suit = r.suit))
  16265.         
  16266.         for r in l.s.rows:
  16267.             s.rows.append(_.RowStack_Class(r.x, r.y, _))
  16268.         
  16269.         for r in l.s.reserves:
  16270.             s.reserves.append(ReserveStack(r.x, r.y, _))
  16271.         
  16272.         l.defaultAll()
  16273.  
  16274.     
  16275.     def startGame(_):
  16276.         for i in range(5):
  16277.             _.s.talon.dealRow(frames = 0)
  16278.         
  16279.         _.startDealSample()
  16280.         _.s.talon.dealRow()
  16281.         r = _.s.rows
  16282.         _.s.talon.dealRow(rows = r[:4])
  16283.         if not __debug__ and len(_.s.talon.cards) == 0:
  16284.             raise AssertionError
  16285.         0
  16286.  
  16287.     
  16288.     def shallHighlightMatch(_, stack1, card1, stack2, card2):
  16289.         if not card1.color != card2.color and card1.rank + 1 == card2.rank:
  16290.             pass
  16291.         return card2.rank + 1 == card1.rank
  16292.  
  16293.  
  16294.  
  16295. class RelaxedFreeCell(FreeCell):
  16296.     RowStack_Class = AC_RowStack
  16297.  
  16298.  
  16299. class ForeCell(FreeCell):
  16300.     RowStack_Class = StackWrapper(FreeCell_AC_RowStack, base_rank = KING)
  16301.     
  16302.     def startGame(_):
  16303.         for i in range(5):
  16304.             _.s.talon.dealRow(frames = 0)
  16305.         
  16306.         _.startDealSample()
  16307.         _.s.talon.dealRow()
  16308.         _.s.talon.dealRow(rows = _.s.reserves)
  16309.         if not __debug__ and len(_.s.talon.cards) == 0:
  16310.             raise AssertionError
  16311.         0
  16312.  
  16313.  
  16314.  
  16315. class Stalactites(FreeCell):
  16316.     Foundation_Class = StackWrapper(RK_FoundationStack, suit = ANY_SUIT, mod = 13, min_cards = 1)
  16317.     RowStack_Class = StackWrapper(BasicRowStack, max_move = 1, max_accept = 0)
  16318.     
  16319.     def createGame(_):
  16320.         FreeCell.createGame(_, reserves = 2)
  16321.  
  16322.     
  16323.     def startGame(_):
  16324.         for i in range(5):
  16325.             _.s.talon.dealRow(frames = 0)
  16326.         
  16327.         _.startDealSample()
  16328.         _.s.talon.dealRow()
  16329.         _.s.talon.dealRow(rows = _.s.foundations)
  16330.         if not __debug__ and len(_.s.talon.cards) == 0:
  16331.             raise AssertionError
  16332.         0
  16333.         _._restoreGameHook(None)
  16334.  
  16335.     
  16336.     def _restoreGameHook(_, game):
  16337.         for s in _.s.foundations:
  16338.             s.cap.base_rank = s.cards[0].rank
  16339.         
  16340.  
  16341.  
  16342. registerGame(GameInfo(5, RelaxedFreeCell, 'Relaxed FreeCell', GI.GT_FREECELL | GI.GT_RELAXED, 1, 0))
  16343. registerGame(GameInfo(8, FreeCell, 'FreeCell', GI.GT_FREECELL, 1, 0))
  16344. registerGame(GameInfo(46, ForeCell, 'ForeCell', GI.GT_FREECELL, 1, 0))
  16345. registerGame(GameInfo(77, Stalactites, 'Stalactites', GI.GT_FREECELL, 1, 0))
  16346.  
  16347. class BakersGame_RowStack(SS_RowStack):
  16348.     
  16349.     def _getMaxMove(_, to_stack_ncards):
  16350.         max_move = getNumberOfFreeStacks(_.game.s.reserves) + 1
  16351.         n = getNumberOfFreeStacks(_.game.s.rows)
  16352.         if to_stack_ncards == 0:
  16353.             n = n - 1
  16354.         
  16355.         while n > 0 and max_move < 1000:
  16356.             max_move = max_move * 2
  16357.             n = n - 1
  16358.         return max_move
  16359.  
  16360.     
  16361.     def canMoveCards(_, cards):
  16362.         max_move = _._getMaxMove(1)
  16363.         if len(cards) <= max_move:
  16364.             pass
  16365.         return SS_RowStack.canMoveCards(_, cards)
  16366.  
  16367.     
  16368.     def acceptsCards(_, from_stack, cards):
  16369.         max_move = _._getMaxMove(len(_.cards))
  16370.         if len(cards) <= max_move:
  16371.             pass
  16372.         return SS_RowStack.acceptsCards(_, from_stack, cards)
  16373.  
  16374.  
  16375.  
  16376. class BakersGame(Game):
  16377.     Layout_Method = Layout.freeCellLayout
  16378.     RowStack_Class = BakersGame_RowStack
  16379.     Hint_Class = FreeCellType_Hint
  16380.     
  16381.     def createGame(_, **layout):
  16382.         (l, s) = (Layout(_), _.s)
  16383.         kwdefault(layout, rows = 8, reserves = 4, texts = 0)
  16384.         apply(_.Layout_Method, (l,), layout)
  16385.         _.setSize(l.size[0], l.size[1])
  16386.         s.talon = InitialDealTalonStack(l.s.talon.x, l.s.talon.y, _)
  16387.         for r in l.s.foundations:
  16388.             _.s.foundations.append(SS_FoundationStack(r.x, r.y, _, suit = r.suit))
  16389.         
  16390.         for r in l.s.rows:
  16391.             s.rows.append(_.RowStack_Class(r.x, r.y, _))
  16392.         
  16393.         for r in l.s.reserves:
  16394.             _.s.reserves.append(ReserveStack(r.x, r.y, _))
  16395.         
  16396.         l.defaultAll()
  16397.  
  16398.     
  16399.     def startGame(_):
  16400.         for i in range(5):
  16401.             _.s.talon.dealRow(frames = 0)
  16402.         
  16403.         _.startDealSample()
  16404.         _.s.talon.dealRow()
  16405.         r = _.s.rows
  16406.         _.s.talon.dealRow(rows = r[:4])
  16407.         if not __debug__ and len(_.s.talon.cards) == 0:
  16408.             raise AssertionError
  16409.         0
  16410.  
  16411.     
  16412.     def shallHighlightMatch(_, stack1, card1, stack2, card2):
  16413.         if not card1.suit == card2.suit and card1.rank + 1 == card2.rank:
  16414.             pass
  16415.         return card2.rank + 1 == card1.rank
  16416.  
  16417.  
  16418.  
  16419. class KingOnlyBakersGame(BakersGame):
  16420.     RowStack_Class = StackWrapper(FreeCell_SS_RowStack, base_rank = KING)
  16421.  
  16422.  
  16423. class EightOff(KingOnlyBakersGame):
  16424.     
  16425.     def createGame(_, rows = 8, reserves = 8):
  16426.         (l, s) = (Layout(_), _.s)
  16427.         h = max(2 * l.YS, l.YS + (16 - 1) * l.YOFFSET)
  16428.         maxrows = max(rows, reserves)
  16429.         _.setSize(l.XM + maxrows * l.XS, l.YM + l.YS + h + l.YS)
  16430.         (x, y) = (l.XM + (maxrows - 4) * l.XS / 2, l.YM)
  16431.         for i in range(4):
  16432.             s.foundations.append(SS_FoundationStack(x, y, _, i))
  16433.             x = x + l.XS
  16434.         
  16435.         (x, y) = (l.XM + (maxrows - rows) * l.XS / 2, y + l.YS)
  16436.         for i in range(rows):
  16437.             s.rows.append(_.RowStack_Class(x, y, _))
  16438.             x = x + l.XS
  16439.         
  16440.         (x, y) = (l.XM + (maxrows - reserves) * l.XS / 2, _.height - l.YS)
  16441.         for i in range(reserves):
  16442.             s.reserves.append(ReserveStack(x, y, _))
  16443.             x = x + l.XS
  16444.         
  16445.         _.setRegion(s.reserves, (-999, y - l.CH / 2, 999999, 999999))
  16446.         s.talon = InitialDealTalonStack(l.XM, l.YM, _)
  16447.         l.defaultStackGroups()
  16448.  
  16449.     
  16450.     def startGame(_):
  16451.         for i in range(5):
  16452.             _.s.talon.dealRow(frames = 0)
  16453.         
  16454.         _.startDealSample()
  16455.         _.s.talon.dealRow()
  16456.         r = _.s.reserves
  16457.         _.s.talon.dealRow(rows = [
  16458.             r[0],
  16459.             r[2],
  16460.             r[4],
  16461.             r[6]])
  16462.         if not __debug__ and len(_.s.talon.cards) == 0:
  16463.             raise AssertionError
  16464.         0
  16465.  
  16466.  
  16467.  
  16468. class SeahavenTowers(KingOnlyBakersGame):
  16469.     
  16470.     def createGame(_):
  16471.         (l, s) = (Layout(_), _.s)
  16472.         h = max(3 * l.YS, 20 * l.YOFFSET)
  16473.         _.setSize(l.XM + 10 * l.XS, l.YM + l.YS + h)
  16474.         (x, y) = (l.XM, l.YM)
  16475.         for i in range(4):
  16476.             s.reserves.append(ReserveStack(x + (i + 3) * l.XS, y, _))
  16477.         
  16478.         for suit in range(4):
  16479.             i = (9, 0, 1, 8)[suit]
  16480.             s.foundations.append(SS_FoundationStack(x + i * l.XS, y, _, suit))
  16481.         
  16482.         (x, y) = (l.XM, l.YM + l.YS)
  16483.         for i in range(10):
  16484.             s.rows.append(_.RowStack_Class(x, y, _))
  16485.             x = x + l.XS
  16486.         
  16487.         _.setRegion(s.rows, (-999, y - l.YM / 2, 999999, 999999))
  16488.         s.talon = InitialDealTalonStack(l.XM, _.height - l.YS, _)
  16489.         _.sg.openstacks = s.foundations + s.rows + s.reserves
  16490.         _.sg.talonstacks = [
  16491.             s.talon]
  16492.         _.sg.dropstacks = s.rows + s.reserves
  16493.         _.sg.reservestacks = s.reserves
  16494.  
  16495.     
  16496.     def startGame(_):
  16497.         for i in range(4):
  16498.             _.s.talon.dealRow(frames = 0)
  16499.         
  16500.         _.startDealSample()
  16501.         _.s.talon.dealRow()
  16502.         _.s.talon.dealRow(rows = _.s.reserves[1:3])
  16503.         if not __debug__ and len(_.s.talon.cards) == 0:
  16504.             raise AssertionError
  16505.         0
  16506.  
  16507.  
  16508.  
  16509. class RelaxedSeahavenTowers(SeahavenTowers):
  16510.     RowStack_Class = KingSS_RowStack
  16511.  
  16512.  
  16513. class Penguin(Game):
  16514.     GAME_VERSION = 2
  16515.     RowStack_Class = SS_RowStack
  16516.     Hint_Class = FreeCellType_Hint
  16517.     
  16518.     def createGame(_, rows = 7, reserves = 7):
  16519.         (l, s) = (Layout(_), _.s)
  16520.         h = max(3 * l.YS, l.YS + (16 - 1) * l.YOFFSET)
  16521.         maxrows = max(rows, reserves)
  16522.         _.setSize(l.XM + (maxrows + 1) * l.XS, l.YM + h + l.YS)
  16523.         _.base_card = None
  16524.         (x, y) = (_.width - l.XS, l.YM)
  16525.         for i in range(4):
  16526.             s.foundations.append(SS_FoundationStack(x, y, _, i, mod = 13, max_move = 0))
  16527.             y = y + l.YS
  16528.         
  16529.         _.setRegion(s.foundations, (x - l.CW / 2, -999, 999999, 999999))
  16530.         (x, y) = (l.XM + (maxrows - rows) * l.XS / 2, l.YM)
  16531.         for i in range(rows):
  16532.             s.rows.append(_.RowStack_Class(x, y, _, mod = 13))
  16533.             x = x + l.XS
  16534.         
  16535.         (x, y) = (l.XM + (maxrows - reserves) * l.XS / 2, _.height - l.YS)
  16536.         for i in range(reserves):
  16537.             s.reserves.append(ReserveStack(x, y, _))
  16538.             x = x + l.XS
  16539.         
  16540.         _.setRegion(s.reserves, (-999, y - l.CH / 2, 999999, 999999))
  16541.         s.talon = InitialDealTalonStack(l.XM + 1, y, _)
  16542.         l.defaultStackGroups()
  16543.  
  16544.     
  16545.     def _shuffleHook(_, cards):
  16546.         return _._shuffleHookMoveToTop(cards, (lambda c, rank = cards[-1].rank: (c.rank == rank, 0)))
  16547.  
  16548.     
  16549.     def startGame(_):
  16550.         _.base_card = _.s.talon.cards[-4]
  16551.         _._updateStacks()
  16552.         for i in range(3):
  16553.             c = _.s.talon.getCard()
  16554.             if not __debug__ and c.rank == _.base_card.rank:
  16555.                 raise AssertionError
  16556.             0
  16557.             to_stack = _.s.foundations[c.suit * _.gameinfo.decks]
  16558.             _.flipMove(_.s.talon)
  16559.             _.moveMove(1, _.s.talon, to_stack, frames = 0)
  16560.         
  16561.         for i in range(6):
  16562.             _.s.talon.dealRow(frames = 0)
  16563.         
  16564.         _.startDealSample()
  16565.         _.s.talon.dealRow()
  16566.         if not __debug__ and len(_.s.talon.cards) == 0:
  16567.             raise AssertionError
  16568.         0
  16569.  
  16570.     
  16571.     def shallHighlightMatch(_, stack1, card1, stack2, card2):
  16572.         if not card1.suit == card2.suit and (card1.rank + 1) % 13 == card2.rank:
  16573.             pass
  16574.         return (card2.rank + 1) % 13 == card1.rank
  16575.  
  16576.     
  16577.     def _restoreGameHook(_, game):
  16578.         _.base_card = _.cards[game.loadinfo.base_card_id]
  16579.         _._updateStacks()
  16580.  
  16581.     
  16582.     def _loadGameHook(_, p):
  16583.         _.loadinfo.addattr(base_card_id = None)
  16584.         _.loadinfo.base_card_id = p.load()
  16585.  
  16586.     
  16587.     def _saveGameHook(_, p):
  16588.         p.dump(_.base_card.id)
  16589.  
  16590.     
  16591.     def _updateStacks(_):
  16592.         for s in _.s.foundations:
  16593.             s.cap.base_rank = _.base_card.rank
  16594.         
  16595.         for s in _.s.rows:
  16596.             s.cap.base_rank = (_.base_card.rank - 1) % 13
  16597.         
  16598.  
  16599.  
  16600. registerGame(GameInfo(45, BakersGame, "Baker's Game", GI.GT_FREECELL, 1, 0))
  16601. registerGame(GameInfo(26, KingOnlyBakersGame, "King Only Baker's Game", GI.GT_FREECELL, 1, 0))
  16602. registerGame(GameInfo(258, EightOff, 'Eight Off', GI.GT_FREECELL, 1, 0))
  16603. registerGame(GameInfo(9, SeahavenTowers, 'Seahaven Towers', GI.GT_FREECELL, 1, 0))
  16604. registerGame(GameInfo(6, RelaxedSeahavenTowers, 'Relaxed Seahaven Towers', GI.GT_FREECELL | GI.GT_RELAXED, 1, 0))
  16605. registerGame(GameInfo(64, Penguin, 'Penguin', GI.GT_FREECELL, 1, 0))
  16606.  
  16607. class EiffelTower_RowStack(OpenStack):
  16608.     
  16609.     def __init__(_, x, y, game):
  16610.         OpenStack.__init__(_, x, y, game, max_move = 0, max_accept = 1)
  16611.         _.CARD_YOFFSET = 1
  16612.  
  16613.     
  16614.     def acceptsCards(_, from_stack, cards):
  16615.         if not OpenStack.acceptsCards(_, from_stack, cards):
  16616.             return 0
  16617.         
  16618.         return _.cards[-1].rank + cards[0].rank == 12
  16619.  
  16620.  
  16621.  
  16622. class EiffelTower(Game):
  16623.     Talon_Class = WasteTalonStack
  16624.     Waste_Class = WasteStack
  16625.     
  16626.     def createGame(_):
  16627.         (l, s) = (Layout(_), _.s)
  16628.         _.setSize(l.XM + 8.5 * l.XS, l.YM + 6 * l.YS)
  16629.         y = l.YM
  16630.         for d in ((1, 2.5), (2, 2), (3, 1.5), (4, 1), (5, 0.5), (5, 0.5)):
  16631.             x = l.XM + d[1] * l.XS
  16632.             for i in range(d[0]):
  16633.                 s.rows.append(EiffelTower_RowStack(x, y, _))
  16634.                 x = x + l.XS
  16635.             
  16636.             y = y + l.YS
  16637.         
  16638.         x = l.XM + 6 * l.XS
  16639.         y = l.YM + 5 * l.YS / 2
  16640.         s.waste = _.Waste_Class(x, y, _)
  16641.         l.createText(s.waste, 'ss')
  16642.         x = x + l.XS
  16643.         s.talon = _.Talon_Class(x, y, _, max_rounds = 1)
  16644.         l.createText(s.talon, 'ss')
  16645.         l.defaultStackGroups()
  16646.  
  16647.     
  16648.     def startGame(_):
  16649.         _.startDealSample()
  16650.         _.s.talon.dealRow()
  16651.         _.s.talon.dealCards()
  16652.  
  16653.     
  16654.     def isGameWon(_):
  16655.         if len(_.s.talon.cards) == 0:
  16656.             pass
  16657.         return len(_.s.waste.cards) == 0
  16658.  
  16659.     
  16660.     def getAutoStacks(_, event = None):
  16661.         return ((), (), ())
  16662.  
  16663.     
  16664.     def shallHighlightMatch(_, stack1, card1, stack2, card2):
  16665.         return card1.rank + card2.rank == 12
  16666.  
  16667.  
  16668.  
  16669. class StrictEiffelTower(EiffelTower):
  16670.     Waste_Class = StackWrapper(WasteStack, max_cards = 2)
  16671.  
  16672. registerGame(GameInfo(16, EiffelTower, 'Eiffel Tower', GI.GT_PAIRING_TYPE, 2, 0))
  16673.  
  16674. class Matriarchy_Waste(WasteStack):
  16675.     
  16676.     def updateText(_):
  16677.         WasteStack.updateText(_)
  16678.         if _.game.s.talon._updateMaxRounds():
  16679.             _.game.s.talon.updateText()
  16680.         
  16681.  
  16682.  
  16683.  
  16684. class Matriarchy_Talon(WasteTalonStack):
  16685.     DEAL = (2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 11, 10, 9, 8, 7, 6, 5)
  16686.     
  16687.     def _updateMaxRounds(_):
  16688.         old = _.max_rounds
  16689.         _.max_rounds = 11
  16690.         rows = _.game.s.rows
  16691.         for i in (0, 2, 4, 6):
  16692.             l1 = len(rows[i + 0].cards) + len(rows[i + 8].cards)
  16693.             l2 = len(rows[i + 1].cards) + len(rows[i + 9].cards)
  16694.             if not __debug__ and l1 + l2 <= 26:
  16695.                 raise AssertionError
  16696.             0
  16697.             if l1 + l2 == 26:
  16698.                 _.max_rounds = _.max_rounds + 2
  16699.             elif l1 >= 13 or l2 >= 13:
  16700.                 _.max_rounds = _.max_rounds + 1
  16701.             
  16702.         
  16703.         if _.max_rounds == 19:
  16704.             _.max_rounds = 18
  16705.         
  16706.         return old != _.max_rounds
  16707.  
  16708.     
  16709.     def canDealCards(_):
  16710.         if _._updateMaxRounds():
  16711.             _.updateText()
  16712.         
  16713.         if not (_.cards) and not (_.game.s.waste.cards):
  16714.             return 0
  16715.         
  16716.         ncards = _.DEAL[_.round - 1]
  16717.         if not __debug__ and ncards > 0:
  16718.             raise AssertionError
  16719.         if not len(_.cards) >= ncards:
  16720.             pass
  16721.         return _.round < _.max_rounds
  16722.  
  16723.     
  16724.     def dealCards(_, sound = 0):
  16725.         ncards = _.DEAL[_.round - 1]
  16726.         if not __debug__ and ncards > 0:
  16727.             raise AssertionError
  16728.         waste = _.game.s.waste
  16729.         n = 0
  16730.         update_flags = 1
  16731.         while n < ncards:
  16732.             while n < ncards:
  16733.                 card = _.getCard()
  16734.                 if not card:
  16735.                     break
  16736.                 
  16737.                 if not __debug__ and not (card.face_up):
  16738.                     raise AssertionError
  16739.                 _.game.flipMove(_)
  16740.                 _.game.moveMove(1, _, waste, frames = 3, shadow = 0)
  16741.                 n = n + 1
  16742.             if n < ncards and len(waste.cards) > 0:
  16743.                 if not __debug__ and len(_.cards) == 0:
  16744.                     raise AssertionError
  16745.                 if __debug__:
  16746.                     if not _.round < _.max_rounds or update_flags == 0:
  16747.                         raise AssertionError
  16748.                 _.game.turnStackMove(waste, _, update_flags = update_flags)
  16749.                 update_flags = 0
  16750.             
  16751.         if not __debug__ and _.round <= _.max_rounds:
  16752.             raise AssertionError
  16753.         if not __debug__ and n == ncards:
  16754.             raise AssertionError
  16755.         if not __debug__ and len(_.game.s.waste.cards) > 0:
  16756.             raise AssertionError
  16757.         return n
  16758.  
  16759.     
  16760.     def updateText(_):
  16761.         if _.game.preview > 1:
  16762.             return None
  16763.         
  16764.         WasteTalonStack.updateText(_, update_rounds = 0)
  16765.         t = 'Round %d/%d' % (_.round, _.max_rounds)
  16766.         _.texts.rounds.config(text = t)
  16767.         t = 'Deal %d' % _.DEAL[_.round - 1]
  16768.         _.texts.misc.config(text = t)
  16769.  
  16770.  
  16771.  
  16772. class Matriarchy_UpRowStack(SS_RowStack):
  16773.     
  16774.     def __init__(_, x, y, game, suit):
  16775.         SS_RowStack.__init__(_, x, y, game, suit = suit, base_rank = KING, mod = 13, dir = 1, min_cards = 1, max_cards = 12)
  16776.         _.CARD_YOFFSET = -(_.CARD_YOFFSET)
  16777.  
  16778.     
  16779.     def getBottomImage(_):
  16780.         return _.game.app.images.getSuitBottom(_.cap.suit)
  16781.  
  16782.  
  16783.  
  16784. class Matriarchy_DownRowStack(SS_RowStack):
  16785.     
  16786.     def __init__(_, x, y, game, suit):
  16787.         SS_RowStack.__init__(_, x, y, game, suit = suit, base_rank = QUEEN, mod = 13, dir = -1, min_cards = 1, max_cards = 12)
  16788.  
  16789.     
  16790.     def getBottomImage(_):
  16791.         return _.game.app.images.getSuitBottom(_.cap.suit)
  16792.  
  16793.  
  16794.  
  16795. class Matriarchy(Game):
  16796.     Hint_Class = CautiousDefaultHint
  16797.     
  16798.     def createGame(_):
  16799.         (l, s) = (Layout(_), _.s)
  16800.         h = max(2 * l.YS, (12 - 1) * l.YOFFSET + l.CH * 2 / 3)
  16801.         _.setSize(10 * l.XS + l.XM, h + l.YM + h)
  16802.         (center, c1, c2) = (_.height / 2, h, _.height - h)
  16803.         (x, y) = (l.XM, c1 - l.CH)
  16804.         for i in range(8):
  16805.             s.rows.append(Matriarchy_UpRowStack(x, y, _, i / 2))
  16806.             x = x + l.XS
  16807.         
  16808.         (x, y) = (l.XM, c2)
  16809.         for i in range(8):
  16810.             s.rows.append(Matriarchy_DownRowStack(x, y, _, i / 2))
  16811.             x = x + l.XS
  16812.         
  16813.         (x, y) = (x + l.XS / 2, c1 - l.CH / 2 - l.CH)
  16814.         tx = x + l.CW / 2
  16815.         s.waste = Matriarchy_Waste(x, y, _)
  16816.         l.createText(s.waste, 'ss')
  16817.         y = c2 + l.CH / 2
  16818.         s.talon = Matriarchy_Talon(x, y, _, max_rounds = VARIABLE_REDEALS)
  16819.         l.createText(s.talon, 'nn')
  16820.         s.talon.texts.rounds = MfxCanvasText(_.canvas, tx, y + l.YS, anchor = 'n')
  16821.         s.talon.texts.misc = MfxCanvasText(_.canvas, tx, center, anchor = 'center', font = getFont('canvas_large'))
  16822.         l.defaultStackGroups()
  16823.  
  16824.     
  16825.     def _shuffleHook(_, cards):
  16826.         return _._shuffleHookMoveToTop(cards, (lambda c: (c.rank == 11, c.suit)), 8)
  16827.  
  16828.     
  16829.     def startGame(_):
  16830.         _.startDealSample()
  16831.         _.s.talon.dealRow(_.s.rows[8:])
  16832.         _.s.talon.dealCards()
  16833.  
  16834.     
  16835.     def isGameWon(_):
  16836.         if len(_.s.talon.cards) == 0:
  16837.             pass
  16838.         return len(_.s.waste.cards) == 0
  16839.  
  16840.     
  16841.     def shallHighlightMatch(_, stack1, card1, stack2, card2):
  16842.         if card1.rank + card2.rank == QUEEN + KING:
  16843.             return 0
  16844.         
  16845.         if not card1.suit == card2.suit and (card1.rank + 1) % 13 == card2.rank:
  16846.             pass
  16847.         return (card2.rank + 1) % 13 == card1.rank
  16848.  
  16849.  
  16850. registerGame(GameInfo(17, Matriarchy, 'Matriarchy', GI.GT_2DECK_TYPE, 2, VARIABLE_REDEALS))
  16851.  
  16852. class Calculation_Hint(DefaultHint):
  16853.     
  16854.     def _getMoveWasteScore(_, score, color, r, t, pile, rpile):
  16855.         if __debug__:
  16856.             if not r is _.game.s.waste and len(pile) == 1:
  16857.                 raise AssertionError
  16858.         score = 30000
  16859.         if len(t.cards) == 0:
  16860.             score = score - (KING - r.cards[0].rank) * 1000
  16861.         elif t.cards[-1].rank < r.cards[0].rank:
  16862.             score = 10000 + t.cards[-1].rank - len(t.cards)
  16863.         elif t.cards[-1].rank == r.cards[0].rank:
  16864.             score = 20000
  16865.         else:
  16866.             score = score - (t.cards[-1].rank - r.cards[0].rank) * 1000
  16867.         return (score, color)
  16868.  
  16869.  
  16870.  
  16871. class BetsyRoss_Foundation(RK_FoundationStack):
  16872.     
  16873.     def updateText(_):
  16874.         if _.game.preview > 1:
  16875.             return None
  16876.         
  16877.         if _.texts.misc:
  16878.             if len(_.cards) == 0:
  16879.                 rank = _.cap.base_rank
  16880.                 _.texts.misc.config(text = RANKS[rank])
  16881.             elif len(_.cards) == _.cap.max_cards:
  16882.                 _.texts.misc.config(text = '')
  16883.             else:
  16884.                 rank = (_.cards[-1].rank + _.cap.dir) % _.cap.mod
  16885.                 _.texts.misc.config(text = RANKS[rank])
  16886.         
  16887.  
  16888.  
  16889.  
  16890. class Calculation_Foundation(BetsyRoss_Foundation):
  16891.     
  16892.     def getBottomImage(_):
  16893.         return _.game.app.images.getLetter(_.cap.base_rank)
  16894.  
  16895.  
  16896.  
  16897. class Calculation_RowStack(BasicRowStack):
  16898.     
  16899.     def acceptsCards(_, from_stack, cards):
  16900.         if not BasicRowStack.acceptsCards(_, from_stack, cards):
  16901.             return 0
  16902.         
  16903.         if from_stack is _.game.s.waste:
  16904.             pass
  16905.         return len(cards) == 1
  16906.  
  16907.     
  16908.     def getBottomImage(_):
  16909.         return _.game.app.images.getReserveBottom()
  16910.  
  16911.  
  16912.  
  16913. class Calculation(Game):
  16914.     Hint_Class = Calculation_Hint
  16915.     
  16916.     def createGame(_):
  16917.         (l, s) = (Layout(_), _.s)
  16918.         h = max(2 * l.YS, 20 * l.YOFFSET)
  16919.         _.setSize(5.5 * l.XS + l.XM, l.YM + l.YS + 30 + h)
  16920.         x0 = l.XM + l.XS * 3 / 2
  16921.         (x, y) = (x0, l.YM)
  16922.         for i in range(4):
  16923.             stack = Calculation_Foundation(x, y, _, base_rank = i, mod = 13, dir = i + 1)
  16924.             s.foundations.append(stack)
  16925.             stack.texts.misc = MfxCanvasText(_.canvas, x + l.CW / 2, y + l.YS, anchor = 'n', font = getFont('canvas_card', cardw = l.CW))
  16926.             x = x + l.XS
  16927.         
  16928.         help = '1: A 2 3 4 5 6 7 8 9 T J Q K\n' + '2: 2 4 6 8 T Q A 3 5 7 9 J K\n' + '3: 3 6 9 Q 2 5 8 J A 4 7 T K\n' + '4: 4 8 Q 3 7 J 2 6 T A 5 9 K'
  16929.         _.texts.help = MfxCanvasText(_.canvas, x + l.XM, y + l.CH / 2, text = help, anchor = 'w', font = getFont('canvas_fixed'))
  16930.         x = x0
  16931.         y = l.YM + l.YS + 30
  16932.         for i in range(4):
  16933.             s.rows.append(Calculation_RowStack(x, y, _, max_move = 1, max_accept = 1))
  16934.             x = x + l.XS
  16935.         
  16936.         _.setRegion(s.rows, (-999, y, 999999, 999999))
  16937.         x = l.XM
  16938.         s.talon = WasteTalonStack(x, y, _, max_rounds = 1)
  16939.         l.createText(s.talon, 'nn')
  16940.         y = y + l.YS
  16941.         s.waste = WasteStack(x, y, _, max_cards = 1)
  16942.         l.defaultStackGroups()
  16943.  
  16944.     
  16945.     def _shuffleHook(_, cards):
  16946.         topcards = [
  16947.             None] * 4
  16948.         for c in cards[:]:
  16949.             pass
  16950.         
  16951.         topcards.reverse()
  16952.         return cards + topcards
  16953.  
  16954.     
  16955.     def startGame(_):
  16956.         _.startDealSample()
  16957.         _.s.talon.dealRow(rows = _.s.foundations)
  16958.         _.s.talon.dealCards()
  16959.  
  16960.     
  16961.     def getHighlightPilesStacks(_):
  16962.         return ()
  16963.  
  16964.  
  16965.  
  16966. class Hopscotch(Calculation):
  16967.     
  16968.     def _shuffleHook(_, cards):
  16969.         topcards = [
  16970.             None] * 4
  16971.         for c in cards[:]:
  16972.             pass
  16973.         
  16974.         topcards.reverse()
  16975.         return cards + topcards
  16976.  
  16977.  
  16978.  
  16979. class BetsyRoss(Calculation):
  16980.     
  16981.     def createGame(_):
  16982.         (l, s) = (Layout(_), _.s)
  16983.         _.setSize(5.5 * l.XS + l.XM, l.YM + l.YS + 30 + 3 * l.YS)
  16984.         x0 = l.XM + l.XS * 3 / 2
  16985.         (x, y) = (x0, l.YM)
  16986.         for i in range(4):
  16987.             stack = BetsyRoss_Foundation(x, y, _, base_rank = i, max_cards = 1, max_move = 0, max_accept = 0)
  16988.             s.foundations.append(stack)
  16989.             x = x + l.XS
  16990.         
  16991.         x = x0
  16992.         y = l.YM + l.YS + 30
  16993.         for i in range(4):
  16994.             stack = BetsyRoss_Foundation(x, y, _, base_rank = 2 * i + 1, mod = 13, dir = i + 1, max_cards = 12, max_move = 0)
  16995.             stack.texts.misc = MfxCanvasText(_.canvas, x + l.CW / 2, y - l.YM, anchor = 's', font = getFont('canvas_card', cardw = l.CW))
  16996.             s.foundations.append(stack)
  16997.             x = x + l.XS
  16998.         
  16999.         help = '1: 2 3 4 5 6 7 8 9 T J Q K\n' + '2: 4 6 8 T Q A 3 5 7 9 J K\n' + '3: 6 9 Q 2 5 8 J A 4 7 T K\n' + '4: 8 Q 3 7 J 2 6 T A 5 9 K'
  17000.         _.texts.help = MfxCanvasText(_.canvas, x + l.XM, y + l.CH / 2, text = help, anchor = 'w', font = getFont('canvas_fixed'))
  17001.         x = l.XM
  17002.         s.talon = WasteTalonStack(x, y, _, max_rounds = 3)
  17003.         l.createText(s.talon, 'nn')
  17004.         y = y + l.YS
  17005.         s.waste = WasteStack(x, y, _)
  17006.         l.createText(s.waste, 'ss')
  17007.         l.defaultStackGroups()
  17008.  
  17009.     
  17010.     def _shuffleHook(_, cards):
  17011.         topcards = [
  17012.             None] * 8
  17013.         for c in cards[:]:
  17014.             if c.rank <= 3 and topcards[c.rank] is None:
  17015.                 topcards[c.rank] = c
  17016.                 cards.remove(c)
  17017.             elif c.rank in (1, 3, 5, 7):
  17018.                 i = 4 + (c.rank - 1) / 2
  17019.                 if topcards[i] is None:
  17020.                     topcards[i] = c
  17021.                     cards.remove(c)
  17022.                 
  17023.             
  17024.         
  17025.         topcards.reverse()
  17026.         return cards + topcards
  17027.  
  17028.  
  17029. registerGame(GameInfo(256, Calculation, 'Calculation', GI.GT_1DECK_TYPE, 1, 0))
  17030. registerGame(GameInfo(94, Hopscotch, 'Hopscotch', GI.GT_1DECK_TYPE, 1, 0))
  17031. registerGame(GameInfo(134, BetsyRoss, 'Betsy Ross', GI.GT_1DECK_TYPE, 1, 2))
  17032.  
  17033. class Canfield_Hint(CautiousDefaultHint):
  17034.     
  17035.     def _getMoveWasteScore(_, score, color, r, t, pile, rpile):
  17036.         (score, color) = CautiousDefaultHint._getMovePileScore(_, score, color, r, t, pile, rpile)
  17037.         return (score + 100000, color)
  17038.  
  17039.  
  17040.  
  17041. class Canfield_AC_RowStack(AC_RowStack):
  17042.     
  17043.     def basicAcceptsCards(_, from_stack, cards):
  17044.         if from_stack in _.game.s.rows:
  17045.             if len(cards) != 1 and len(cards) != len(from_stack.cards):
  17046.                 return 0
  17047.             
  17048.         
  17049.         return AC_RowStack.basicAcceptsCards(_, from_stack, cards)
  17050.  
  17051.  
  17052.  
  17053. class Canfield_SS_RowStack(SS_RowStack):
  17054.     
  17055.     def basicAcceptsCards(_, from_stack, cards):
  17056.         if from_stack in _.game.s.rows:
  17057.             if len(cards) != 1 and len(cards) != len(from_stack.cards):
  17058.                 return 0
  17059.             
  17060.         
  17061.         return SS_RowStack.basicAcceptsCards(_, from_stack, cards)
  17062.  
  17063.  
  17064.  
  17065. class Canfield_RK_RowStack(RK_RowStack):
  17066.     
  17067.     def basicAcceptsCards(_, from_stack, cards):
  17068.         if from_stack in _.game.s.rows:
  17069.             if len(cards) != 1 and len(cards) != len(from_stack.cards):
  17070.                 return 0
  17071.             
  17072.         
  17073.         return RK_RowStack.basicAcceptsCards(_, from_stack, cards)
  17074.  
  17075.  
  17076.  
  17077. class Canfield(Game):
  17078.     Foundation_Class = SS_FoundationStack
  17079.     RowStack_Class = StackWrapper(Canfield_AC_RowStack, mod = 13)
  17080.     ReserveStack_Class = OpenStack
  17081.     Hint_Class = Canfield_Hint
  17082.     INITIAL_RESERVE_CARDS = 13
  17083.     INITIAL_RESERVE_FACEUP = 0
  17084.     FILL_EMPTY_ROWS = 1
  17085.     
  17086.     def createGame(_, rows = 4, max_rounds = -1, num_deal = 3):
  17087.         (l, s) = (Layout(_), _.s)
  17088.         decks = _.gameinfo.decks
  17089.         h = max(3 * l.YS, 20 * l.YOFFSET)
  17090.         _.setSize(l.XM + (2 + max(rows, 4 * decks)) * l.XS + l.XM, l.YM + l.YS + 20 + h)
  17091.         _.base_card = None
  17092.         (x, y) = (l.XM, l.YM)
  17093.         s.talon = WasteTalonStack(x, y, _, max_rounds = max_rounds, num_deal = num_deal)
  17094.         l.createText(s.talon, 's')
  17095.         x = x + l.XS
  17096.         s.waste = WasteStack(x, y, _)
  17097.         l.createText(s.waste, 's')
  17098.         x = x + l.XM
  17099.         for i in range(4):
  17100.             for j in range(decks):
  17101.                 x = x + l.XS
  17102.                 s.foundations.append(_.Foundation_Class(x, y, _, i, mod = 13, max_move = 0))
  17103.             
  17104.         
  17105.         font = getFont('canvas_card', cardw = l.CW)
  17106.         _.texts.info = MfxCanvasText(_.canvas, tx, ty, anchor = ta, font = font)
  17107.         (x, y) = (l.XM, l.YM + l.YS + 20)
  17108.         s.reserves.append(_.ReserveStack_Class(x, y, _))
  17109.         s.reserves[0].CARD_YOFFSET = 10
  17110.         x = l.XM + 2 * l.XS + l.XM
  17111.         for i in range(rows):
  17112.             s.rows.append(_.RowStack_Class(x, y, _))
  17113.             x = x + l.XS
  17114.         
  17115.         l.defaultStackGroups()
  17116.  
  17117.     
  17118.     def updateText(_):
  17119.         if _.preview > 1:
  17120.             return None
  17121.         
  17122.         if not (_.base_card):
  17123.             t = ''
  17124.         else:
  17125.             t = RANKS[_.base_card.rank]
  17126.         _.texts.info.config(text = t)
  17127.  
  17128.     
  17129.     def startGame(_):
  17130.         _.startDealSample()
  17131.         _.base_card = None
  17132.         _.updateText()
  17133.         _.base_card = _.s.talon.getCard()
  17134.         for s in _.s.foundations:
  17135.             s.cap.base_rank = _.base_card.rank
  17136.         
  17137.         n = _.base_card.suit * _.gameinfo.decks
  17138.         if _.s.foundations[n].cards:
  17139.             if not __debug__ and _.gameinfo.decks > 1:
  17140.                 raise AssertionError
  17141.             0
  17142.             n = n + 1
  17143.         
  17144.         _.flipMove(_.s.talon)
  17145.         _.moveMove(1, _.s.talon, _.s.foundations[n])
  17146.         _.updateText()
  17147.         for i in range(_.INITIAL_RESERVE_CARDS):
  17148.             _.moveMove(1, _.s.talon, _.s.reserves[0], frames = 4, shadow = 0)
  17149.         
  17150.         if _.s.reserves[0].canFlipCard():
  17151.             _.flipMove(_.s.reserves[0])
  17152.         
  17153.         _.s.talon.dealRow(reverse = 1)
  17154.         _.s.talon.dealCards()
  17155.  
  17156.     
  17157.     def fillStack(_, stack):
  17158.         if stack in _.s.rows and _.s.reserves:
  17159.             if _.FILL_EMPTY_ROWS:
  17160.                 if not (stack.cards) and _.s.reserves[0].cards:
  17161.                     if not (_.s.reserves[0].cards[-1].face_up):
  17162.                         _.s.reserves[0].flipMove()
  17163.                     
  17164.                     _.s.reserves[0].moveMove(1, stack)
  17165.                 
  17166.             
  17167.         elif stack in _.s.reserves:
  17168.             if stack.canFlipCard():
  17169.                 stack.flipMove()
  17170.             
  17171.         
  17172.  
  17173.     
  17174.     def shallHighlightMatch(_, stack1, card1, stack2, card2):
  17175.         if not card1.color != card2.color and (card1.rank + 1) % 13 == card2.rank:
  17176.             pass
  17177.         return (card2.rank + 1) % 13 == card1.rank
  17178.  
  17179.     
  17180.     def _restoreGameHook(_, game):
  17181.         _.base_card = _.cards[game.loadinfo.base_card_id]
  17182.         for s in _.s.foundations:
  17183.             s.cap.base_rank = _.base_card.rank
  17184.         
  17185.  
  17186.     
  17187.     def _loadGameHook(_, p):
  17188.         _.loadinfo.addattr(base_card_id = None)
  17189.         _.loadinfo.base_card_id = p.load()
  17190.  
  17191.     
  17192.     def _saveGameHook(_, p):
  17193.         p.dump(_.base_card.id)
  17194.  
  17195.  
  17196.  
  17197. class SuperiorCanfield(Canfield):
  17198.     INITIAL_RESERVE_FACEUP = 1
  17199.     FILL_EMPTY_ROWS = 0
  17200.  
  17201.  
  17202. class Rainfall(Canfield):
  17203.     
  17204.     def createGame(_):
  17205.         Canfield.createGame(_, max_rounds = 3, num_deal = 1)
  17206.  
  17207.  
  17208.  
  17209. class Rainbow(Canfield):
  17210.     RowStack_Class = StackWrapper(Canfield_RK_RowStack, mod = 13)
  17211.     
  17212.     def createGame(_):
  17213.         Canfield.createGame(_, max_rounds = 1, num_deal = 1)
  17214.  
  17215.  
  17216.  
  17217. class Storehouse(Canfield):
  17218.     RowStack_Class = StackWrapper(Canfield_SS_RowStack, mod = 13)
  17219.     
  17220.     def createGame(_):
  17221.         Canfield.createGame(_, max_rounds = 3, num_deal = 1)
  17222.  
  17223.     
  17224.     def _shuffleHook(_, cards):
  17225.         return _._shuffleHookMoveToTop(cards, (lambda c: (c.rank == 1, c.suit)))
  17226.  
  17227.     
  17228.     def startGame(_):
  17229.         _.startDealSample()
  17230.         _.s.talon.dealRow(rows = _.s.foundations[:3])
  17231.         Canfield.startGame(_)
  17232.  
  17233.     
  17234.     def shallHighlightMatch(_, stack1, card1, stack2, card2):
  17235.         if not card1.suit == card2.suit and (card1.rank + 1) % 13 == card2.rank:
  17236.             pass
  17237.         return (card2.rank + 1) % 13 == card1.rank
  17238.  
  17239.     
  17240.     def updateText(_):
  17241.         pass
  17242.  
  17243.  
  17244.  
  17245. class Chameleon(Canfield):
  17246.     RowStack_Class = StackWrapper(Canfield_RK_RowStack, mod = 13)
  17247.     INITIAL_RESERVE_CARDS = 12
  17248.     
  17249.     def createGame(_):
  17250.         Canfield.createGame(_, rows = 3, max_rounds = 1, num_deal = 1)
  17251.  
  17252.     
  17253.     def shallHighlightMatch(_, stack1, card1, stack2, card2):
  17254.         if not (card1.rank + 1) % 13 == card2.rank:
  17255.             pass
  17256.         return (card2.rank + 1) % 13 == card1.rank
  17257.  
  17258.  
  17259.  
  17260. class DoubleCanfield(Canfield):
  17261.     
  17262.     def createGame(_):
  17263.         Canfield.createGame(_, rows = 5)
  17264.  
  17265.  
  17266.  
  17267. class AmericanToad(Canfield):
  17268.     RowStack_Class = StackWrapper(Canfield_SS_RowStack, mod = 13)
  17269.     INITIAL_RESERVE_CARDS = 20
  17270.     INITIAL_RESERVE_FACEUP = 1
  17271.     
  17272.     def createGame(_):
  17273.         Canfield.createGame(_, rows = 8, max_rounds = 2, num_deal = 1)
  17274.  
  17275.  
  17276.  
  17277. class VariegatedCanfield(Canfield):
  17278.     RowStack_Class = Canfield_AC_RowStack
  17279.     INITIAL_RESERVE_FACEUP = 1
  17280.     
  17281.     def createGame(_):
  17282.         Canfield.createGame(_, rows = 5, max_rounds = 3)
  17283.  
  17284.     
  17285.     def _shuffleHook(_, cards):
  17286.         return _._shuffleHookMoveToTop(cards, (lambda c: (c.rank == 0, c.suit)))
  17287.  
  17288.     
  17289.     def startGame(_):
  17290.         _.startDealSample()
  17291.         _.s.talon.dealRow(rows = _.s.foundations[:7])
  17292.         Canfield.startGame(_)
  17293.  
  17294.     
  17295.     def shallHighlightMatch(_, stack1, card1, stack2, card2):
  17296.         if not card1.color != card2.color and card1.rank + 1 == card2.rank:
  17297.             pass
  17298.         return card2.rank + 1 == card1.rank
  17299.  
  17300.     
  17301.     def updateText(_):
  17302.         pass
  17303.  
  17304.  
  17305.  
  17306. class EagleWing_ReserveStack(OpenStack):
  17307.     
  17308.     def canFlipCard(_):
  17309.         if len(_.cards) == 1:
  17310.             pass
  17311.         return not (_.cards[-1].face_up)
  17312.  
  17313.  
  17314.  
  17315. class EagleWing(Canfield):
  17316.     RowStack_Class = StackWrapper(SS_RowStack, mod = 13, max_move = 1, max_cards = 3)
  17317.     ReserveStack_Class = EagleWing_ReserveStack
  17318.     
  17319.     def createGame(_):
  17320.         (l, s) = (Layout(_), _.s)
  17321.         _.setSize(l.XM + 9 * l.XS + l.XM, l.YM + 4 * l.YS)
  17322.         _.base_card = None
  17323.         (x, y) = (l.XM, l.YM)
  17324.         s.talon = WasteTalonStack(x, y, _, max_rounds = 3, num_deal = 1)
  17325.         l.createText(s.talon, 'ss')
  17326.         x = x + l.XS
  17327.         s.waste = WasteStack(x, y, _)
  17328.         l.createText(s.waste, 'ss')
  17329.         for i in range(4):
  17330.             x = l.XM + (i + 3) * l.XS
  17331.             s.foundations.append(_.Foundation_Class(x, y, _, i, mod = 13, max_move = 0))
  17332.         
  17333.         (tx, ty, ta, tf) = l.getTextAttr(None, 'se')
  17334.         (tx, ty) = (x + tx + l.XM, y + ty)
  17335.         font = getFont('canvas_card', cardw = l.CW)
  17336.         _.texts.info = MfxCanvasText(_.canvas, tx, ty, anchor = ta, font = font)
  17337.         ry = l.YM + 2 * l.YS
  17338.         for i in range(8):
  17339.             x = l.XM + (i + (i >= 4)) * l.XS
  17340.             y = ry - (0.2, 0.4, 0.6, 0.4, 0.4, 0.6, 0.4, 0.2)[i] * l.CH
  17341.             s.rows.append(_.RowStack_Class(x, y, _))
  17342.         
  17343.         (x, y) = (l.XM + 4 * l.XS, ry)
  17344.         s.reserves.append(_.ReserveStack_Class(x, y, _))
  17345.         l.createText(s.reserves[0], 'ss')
  17346.         l.defaultStackGroups()
  17347.  
  17348.  
  17349. registerGame(GameInfo(105, Canfield, 'Canfield', GI.GT_CANFIELD | GI.GT_CONTRIB, 1, -1))
  17350. registerGame(GameInfo(101, SuperiorCanfield, 'Superior Canfield', GI.GT_CANFIELD, 1, -1))
  17351. registerGame(GameInfo(99, Rainfall, 'Rainfall', GI.GT_CANFIELD | GI.GT_ORIGINAL, 1, 2))
  17352. registerGame(GameInfo(108, Rainbow, 'Rainbow', GI.GT_CANFIELD, 1, 0))
  17353. registerGame(GameInfo(100, Storehouse, 'Storehouse', GI.GT_CANFIELD, 1, 2, altnames = 'Straight Up'))
  17354. registerGame(GameInfo(43, Chameleon, 'Chameleon', GI.GT_CANFIELD, 1, 0, altnames = 'Kansas'))
  17355. registerGame(GameInfo(106, DoubleCanfield, 'Double Canfield', GI.GT_CANFIELD, 2, -1))
  17356. registerGame(GameInfo(103, AmericanToad, 'American Toad', GI.GT_CANFIELD, 2, 1))
  17357. registerGame(GameInfo(102, VariegatedCanfield, 'Variegated Canfield', GI.GT_CANFIELD, 2, 2))
  17358. registerGame(GameInfo(112, EagleWing, 'Eagle Wing', GI.GT_CANFIELD, 1, 2))
  17359.  
  17360. class Golf_Hint(AbstractHint):
  17361.     
  17362.     def computeHints(_):
  17363.         game = _.game
  17364.         for r in game.sg.dropstacks:
  17365.             (w, ncards) = r.canDropCards(game.s.foundations)
  17366.             if __debug__:
  17367.                 if not w is game.s.waste and ncards == 1:
  17368.                     raise AssertionError
  17369.             None if not w else game.sg.dropstacks
  17370.             ww = (_.ClonedStack(w, stackcards = w.cards + [
  17371.                 r.cards[-1]]),)
  17372.             (score, color) = (10000 + r.id, None)
  17373.             for t in game.sg.dropstacks:
  17374.                 if t is r:
  17375.                     t = _.ClonedStack(r, stackcards = r.cards[:-1])
  17376.                 
  17377.                 if t.canFlipCard():
  17378.                     score = score + 100
  17379.                 elif t.canDropCards(ww)[0]:
  17380.                     score = score + 100
  17381.                 
  17382.             
  17383.             _.addHint(score, ncards, r, w, color)
  17384.         
  17385.  
  17386.  
  17387.  
  17388. class Golf_RowStack(BasicRowStack):
  17389.     
  17390.     def clickHandler(_, event):
  17391.         return _.doubleclickHandler(event)
  17392.  
  17393.  
  17394.  
  17395. class Golf_Waste(WasteStack):
  17396.     
  17397.     def __init__(_, x, y, game, **cap):
  17398.         kwdefault(cap, max_move = 0, max_accept = 1)
  17399.         apply(WasteStack.__init__, (_, x, y, game), cap)
  17400.  
  17401.     
  17402.     def acceptsCards(_, from_stack, cards):
  17403.         if not WasteStack.acceptsCards(_, from_stack, cards):
  17404.             return 0
  17405.         
  17406.         (r1, r2) = (_.cards[-1].rank, cards[0].rank)
  17407.         if _.game.getStrictness() == 1:
  17408.             if r1 == KING:
  17409.                 return 0
  17410.             
  17411.         
  17412.         if not (r1 + 1) % _.cap.mod == r2:
  17413.             pass
  17414.         return (r2 + 1) % _.cap.mod == r1
  17415.  
  17416.  
  17417.  
  17418. class Golf(Game):
  17419.     Waste_Class = Golf_Waste
  17420.     Hint_Class = Golf_Hint
  17421.     
  17422.     def createGame(_):
  17423.         (l, s) = (Layout(_, XOFFSET = 10), _.s)
  17424.         (w1, w2) = (8 * l.XS + l.XM, 2 * l.XS)
  17425.         if w2 + 52 * l.XOFFSET > w1:
  17426.             l.XOFFSET = int((w1 - w2) / 52)
  17427.         
  17428.         _.setSize(w1, 4 * l.YS + l.YM)
  17429.         (x, y) = (l.XM + l.XS / 2, l.YM)
  17430.         for i in range(7):
  17431.             s.rows.append(Golf_RowStack(x, y, _))
  17432.             x = x + l.XS
  17433.         
  17434.         (x, y) = (l.XM, _.height - l.YS)
  17435.         s.talon = WasteTalonStack(x, y, _, max_rounds = 1)
  17436.         l.createText(s.talon, 'nn')
  17437.         x = x + l.XS
  17438.         s.waste = _.Waste_Class(x, y, _)
  17439.         s.waste.CARD_XOFFSET = l.XOFFSET
  17440.         l.createText(s.waste, 'nn')
  17441.         s.foundations.append(s.waste)
  17442.         _.sg.openstacks = [
  17443.             s.waste]
  17444.         _.sg.talonstacks = [
  17445.             s.talon]
  17446.         _.sg.dropstacks = s.rows
  17447.  
  17448.     
  17449.     def startGame(_):
  17450.         for i in range(4):
  17451.             _.s.talon.dealRow(frames = 0)
  17452.         
  17453.         _.startDealSample()
  17454.         _.s.talon.dealRow()
  17455.         _.s.talon.dealCards()
  17456.  
  17457.     
  17458.     def shallHighlightMatch(_, stack1, card1, stack2, card2):
  17459.         if not card1.rank + 1 == card2.rank:
  17460.             pass
  17461.         return card2.rank + 1 == card1.rank
  17462.  
  17463.     
  17464.     def getHighlightPilesStacks(_):
  17465.         return ()
  17466.  
  17467.     
  17468.     def getAutoStacks(_, event = None):
  17469.         if event is None:
  17470.             return (_.sg.dropstacks, (), ())
  17471.         else:
  17472.             return (_.sg.dropstacks, _.sg.dropstacks, ())
  17473.  
  17474.  
  17475.  
  17476. class DeadKingGolf(Golf):
  17477.     
  17478.     def getStrictness(_):
  17479.         return 1
  17480.  
  17481.     
  17482.     def shallHighlightMatch(_, stack1, card1, stack2, card2):
  17483.         if card1.rank == KING:
  17484.             return 0
  17485.         
  17486.         return Golf.shallHighlightMatch(_, stack1, card1, stack2, card2)
  17487.  
  17488.  
  17489.  
  17490. class RelaxedGolf(Golf):
  17491.     Waste_Class = StackWrapper(Golf_Waste, mod = 13)
  17492.     
  17493.     def shallHighlightMatch(_, stack1, card1, stack2, card2):
  17494.         if not (card1.rank + 1) % 13 == card2.rank:
  17495.             pass
  17496.         return (card2.rank + 1) % 13 == card1.rank
  17497.  
  17498.  
  17499.  
  17500. class Elevator_RowStack(Golf_RowStack):
  17501.     STEP = (1, 2, 2, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6)
  17502.     
  17503.     def basicIsBlocked(_):
  17504.         (r, step) = (_.game.s.rows, _.STEP)
  17505.         (i, n) = (_.id, 1)
  17506.         while i < 21:
  17507.             i = i + step[i]
  17508.             n = n + 1
  17509.             for j in range(i, i + n):
  17510.                 pass
  17511.             
  17512.             continue
  17513.             None if r[j].cards else range(i, i + n)
  17514.         return 0
  17515.  
  17516.  
  17517.  
  17518. class Elevator(RelaxedGolf):
  17519.     
  17520.     def createGame(_):
  17521.         (l, s) = (Layout(_), _.s)
  17522.         _.setSize(9 * l.XS + l.XM, 4 * l.YS + l.YM)
  17523.         for i in range(7):
  17524.             x = l.XM + (8 - i) * l.XS / 2
  17525.             y = l.YM + i * l.YS / 2
  17526.             for j in range(i + 1):
  17527.                 s.rows.append(Elevator_RowStack(x, y, _))
  17528.                 x = x + l.XS
  17529.             
  17530.         
  17531.         (x, y) = (l.XM, l.YM)
  17532.         s.talon = WasteTalonStack(x, y, _, max_rounds = 1)
  17533.         l.createText(s.talon, 'ss')
  17534.         x = x + l.XS
  17535.         s.waste = _.Waste_Class(x, y, _)
  17536.         l.createText(s.waste, 'ss')
  17537.         s.foundations.append(s.waste)
  17538.         _.sg.openstacks = [
  17539.             s.waste]
  17540.         _.sg.talonstacks = [
  17541.             s.talon]
  17542.         _.sg.dropstacks = s.rows
  17543.  
  17544.     
  17545.     def startGame(_):
  17546.         _.startDealSample()
  17547.         _.s.talon.dealRow(rows = _.s.rows[:21], flip = 0)
  17548.         _.s.talon.dealRow(rows = _.s.rows[21:])
  17549.         _.s.talon.dealCards()
  17550.  
  17551.  
  17552. registerGame(GameInfo(36, Golf, 'Golf', GI.GT_GOLF, 1, 0))
  17553. registerGame(GameInfo(259, DeadKingGolf, 'Dead King Golf', GI.GT_GOLF, 1, 0))
  17554. registerGame(GameInfo(260, RelaxedGolf, 'Relaxed Golf', GI.GT_GOLF | GI.GT_RELAXED, 1, 0))
  17555. registerGame(GameInfo(40, Elevator, 'Elevator', GI.GT_GOLF, 1, 0))
  17556.  
  17557. class GrandfathersClock_Hint(CautiousDefaultHint):
  17558.     
  17559.     def _getDropCardScore(_, score, color, r, t, ncards):
  17560.         return (92000, color)
  17561.  
  17562.  
  17563.  
  17564. class GrandfathersClock(Game):
  17565.     Hint_Class = GrandfathersClock_Hint
  17566.     
  17567.     def createGame(_):
  17568.         (l, s) = (Layout(_, XOFFSET = 10), _.s)
  17569.         dh = max(3 * l.YS / 2 + l.CH, l.YS + (9 - 1) * l.YOFFSET)
  17570.         _.setSize(10 * l.XS + l.XM, l.YM + 2 * dh)
  17571.         for i in range(2):
  17572.             (x, y) = (l.XM, l.YM + i * dh)
  17573.             for j in range(4):
  17574.                 s.rows.append(RK_RowStack(x, y, _, max_move = 1, max_accept = 1))
  17575.                 x = x + l.XS
  17576.             
  17577.         
  17578.         y = l.YM + dh - l.CH / 2
  17579.         _.setRegion(s.rows[:4], (-999, -999, x - l.XM / 2, y))
  17580.         _.setRegion(s.rows[4:], (-999, y, x - l.XM / 2, 999999))
  17581.         d = [
  17582.             (0, 0),
  17583.             (1, 0.15),
  17584.             (2, 0.5),
  17585.             (2.5, 1.5),
  17586.             (2, 2.5),
  17587.             (1, 2.85)]
  17588.         for i in range(len(d)):
  17589.             d.append((0 - d[i][0], 3 - d[i][1]))
  17590.         
  17591.         (x0, y0) = (l.XM, l.YM + dh - l.CH)
  17592.         for i in range(12):
  17593.             j = (i + 5) % 12
  17594.             x = int(round(x0 + (6.5 + d[j][0]) * l.XS))
  17595.             y = int(round(y0 + (-1.5 + d[j][1]) * l.YS))
  17596.             suit = (1, 2, 0, 3)[i % 4]
  17597.             s.foundations.append(SS_FoundationStack(x, y, _, suit, base_rank = i + 1, mod = 13, max_move = 0))
  17598.         
  17599.         s.talon = InitialDealTalonStack(_.width - l.XS, _.height - l.YS, _)
  17600.         _.sg.openstacks = s.foundations + s.rows
  17601.         _.sg.talonstacks = [
  17602.             s.talon]
  17603.         _.sg.dropstacks = s.rows
  17604.  
  17605.     
  17606.     def _shuffleHook(_, cards):
  17607.         (C, S, H, D) = (0 * 13, 1 * 13, 2 * 13, 3 * 13)
  17608.         ids = (1 + S, 2 + H, 3 + C, 4 + D, 5 + S, 6 + H, 7 + C, 8 + D, 9 + S, 10 + H, 11 + C, 12 + D)
  17609.         clocks = []
  17610.         for c in cards[:]:
  17611.             pass
  17612.         
  17613.         clocks.sort((lambda a, b: cmp(b.rank, a.rank)))
  17614.         return clocks + cards
  17615.  
  17616.     
  17617.     def startGame(_):
  17618.         _.playSample('grandfathersclock', loop = 1)
  17619.         for i in range(4):
  17620.             _.s.talon.dealRow(frames = 0)
  17621.         
  17622.         _.s.talon.dealRow()
  17623.         _.s.talon.dealRow(rows = _.s.foundations)
  17624.         if not __debug__ and len(_.s.talon.cards) == 0:
  17625.             raise AssertionError
  17626.         0
  17627.  
  17628.     
  17629.     def shallHighlightMatch(_, stack1, card1, stack2, card2):
  17630.         if not card1.rank + 1 == card2.rank:
  17631.             pass
  17632.         return card2.rank + 1 == card1.rank
  17633.  
  17634.     
  17635.     def getHighlightPilesStacks(_):
  17636.         return ()
  17637.  
  17638.     
  17639.     def getAutoStacks(_, event = None):
  17640.         return ((), (), ())
  17641.  
  17642.  
  17643. registerGame(GameInfo(261, GrandfathersClock, "Grandfather's Clock", GI.GT_1DECK_TYPE, 1, 0))
  17644.  
  17645. class Numerica_Hint(DefaultHint):
  17646.     
  17647.     def _getMoveWasteScore(_, score, color, r, t, pile, rpile):
  17648.         if __debug__:
  17649.             if not r is _.game.s.waste and len(pile) == 1:
  17650.                 raise AssertionError
  17651.         score = 30000
  17652.         if len(t.cards) == 0:
  17653.             score = score - (KING - r.cards[0].rank) * 1000
  17654.         elif t.cards[-1].rank < r.cards[0].rank:
  17655.             score = 10000 + t.cards[-1].rank - len(t.cards)
  17656.         elif t.cards[-1].rank == r.cards[0].rank:
  17657.             score = 20000
  17658.         else:
  17659.             score = score - (t.cards[-1].rank - r.cards[0].rank) * 1000
  17660.         return (score, color)
  17661.  
  17662.  
  17663.  
  17664. class Numerica_RowStack(BasicRowStack):
  17665.     
  17666.     def acceptsCards(_, from_stack, cards):
  17667.         if not BasicRowStack.acceptsCards(_, from_stack, cards):
  17668.             return 0
  17669.         
  17670.         if from_stack is _.game.s.waste:
  17671.             pass
  17672.         return len(cards) == 1
  17673.  
  17674.     
  17675.     def getBottomImage(_):
  17676.         return _.game.app.images.getReserveBottom()
  17677.  
  17678.  
  17679.  
  17680. class Numerica(Game):
  17681.     Hint_Class = Numerica_Hint
  17682.     Foundation_Class = StackWrapper(RK_FoundationStack, suit = ANY_SUIT)
  17683.     
  17684.     def createGame(_, rows = 4):
  17685.         (l, s) = (Layout(_), _.s)
  17686.         h = max(2 * l.YS, 20 * l.YOFFSET)
  17687.         _.setSize(l.XM + (1.5 + rows) * l.XS + l.XM, l.YM + l.YS + h)
  17688.         x0 = l.XM + l.XS * 3 / 2
  17689.         (x, y) = (x0 + (rows - 4) * l.XS / 2, l.YM)
  17690.         for i in range(4):
  17691.             s.foundations.append(_.Foundation_Class(x, y, _, suit = i))
  17692.             x = x + l.XS
  17693.         
  17694.         (x, y) = (x0, l.YM + l.YS)
  17695.         for i in range(rows):
  17696.             s.rows.append(Numerica_RowStack(x, y, _, max_accept = 1))
  17697.             x = x + l.XS
  17698.         
  17699.         _.setRegion(s.rows, (x0 - l.XS / 2, y, 999999, 999999))
  17700.         x = l.XM
  17701.         s.talon = WasteTalonStack(x, y, _, max_rounds = 1)
  17702.         s.talon.texts.ncards = MfxCanvasText(_.canvas, x + l.CW / 2, y - l.YM, anchor = 's')
  17703.         y = y + l.YS
  17704.         s.waste = WasteStack(x, y, _, max_cards = 1)
  17705.         _.sg.openstacks = s.foundations + s.rows
  17706.         _.sg.talonstacks = [
  17707.             s.talon] + [
  17708.             s.waste]
  17709.         _.sg.dropstacks = s.rows + [
  17710.             s.waste]
  17711.  
  17712.     
  17713.     def startGame(_):
  17714.         _.startDealSample()
  17715.         _.s.talon.dealCards()
  17716.  
  17717.     
  17718.     def shallHighlightMatch(_, stack1, card1, stack2, card2):
  17719.         if not card1.suit == card2.suit and card1.rank + 1 == card2.rank:
  17720.             pass
  17721.         return card2.rank + 1 == card1.rank
  17722.  
  17723.     
  17724.     def getHighlightPilesStacks(_):
  17725.         return ()
  17726.  
  17727.  
  17728.  
  17729. class LadyBetty(Numerica):
  17730.     Foundation_Class = SS_FoundationStack
  17731.     
  17732.     def createGame(_):
  17733.         Numerica.createGame(_, rows = 6)
  17734.  
  17735.  
  17736. registerGame(GameInfo(257, Numerica, 'Numerica', GI.GT_NUMERICA | GI.GT_CONTRIB, 1, 0, altnames = 'Sir Tommy'))
  17737. registerGame(GameInfo(171, LadyBetty, 'Lady Betty', GI.GT_NUMERICA, 1, 0))
  17738.  
  17739. class Yukon_Hint(YukonType_Hint):
  17740.     BONUS_FLIP_CARD = 9000
  17741.     BONUS_CREATE_EMPTY_ROW = 100
  17742.     
  17743.     def _getMovePileScore(_, score, color, r, t, pile, rpile):
  17744.         (s, color) = YukonType_Hint._getMovePileScore(_, score, color, r, t, pile, rpile)
  17745.         bonus = s - score
  17746.         if __debug__:
  17747.             if bonus <= bonus:
  17748.                 pass
  17749.             elif not bonus <= 9999:
  17750.                 raise AssertionError
  17751.         tpile = t.getPile()
  17752.         return (score + bonus, color)
  17753.  
  17754.  
  17755.  
  17756. class Yukon(Game):
  17757.     Layout_Method = Layout.yukonLayout
  17758.     Talon_Class = InitialDealTalonStack
  17759.     Foundation_Class = SS_FoundationStack
  17760.     RowStack_Class = StackWrapper(Yukon_AC_RowStack, base_rank = KING)
  17761.     Hint_Class = Yukon_Hint
  17762.     
  17763.     def createGame(_, **layout):
  17764.         (l, s) = (Layout(_), _.s)
  17765.         kwdefault(layout, rows = 7, texts = 0)
  17766.         apply(_.Layout_Method, (l,), layout)
  17767.         _.setSize(l.size[0], l.size[1])
  17768.         s.talon = _.Talon_Class(l.s.talon.x, l.s.talon.y, _)
  17769.         for r in l.s.foundations:
  17770.             s.foundations.append(_.Foundation_Class(r.x, r.y, _, suit = r.suit, max_move = 0))
  17771.         
  17772.         for r in l.s.rows:
  17773.             s.rows.append(_.RowStack_Class(r.x, r.y, _))
  17774.         
  17775.         l.defaultAll()
  17776.         return l
  17777.  
  17778.     
  17779.     def startGame(_):
  17780.         for i in range(1, len(_.s.rows)):
  17781.             _.s.talon.dealRow(rows = _.s.rows[i:], flip = 0, frames = 0)
  17782.         
  17783.         for i in range(4):
  17784.             _.s.talon.dealRow(rows = _.s.rows[1:], flip = 1, frames = 0)
  17785.         
  17786.         _.startDealSample()
  17787.         _.s.talon.dealRow()
  17788.         if not __debug__ and len(_.s.talon.cards) == 0:
  17789.             raise AssertionError
  17790.         0
  17791.  
  17792.     
  17793.     def getHighlightPilesStacks(_):
  17794.         return ()
  17795.  
  17796.     
  17797.     def shallHighlightMatch(_, stack1, card1, stack2, card2):
  17798.         if not card1.color != card2.color and card1.rank + 1 == card2.rank:
  17799.             pass
  17800.         return card2.rank + 1 == card1.rank
  17801.  
  17802.  
  17803.  
  17804. class RussianSolitaire(Yukon):
  17805.     RowStack_Class = StackWrapper(Yukon_SS_RowStack, base_rank = KING)
  17806.     
  17807.     def shallHighlightMatch(_, stack1, card1, stack2, card2):
  17808.         if not card1.suit == card2.suit and card1.rank + 1 == card2.rank:
  17809.             pass
  17810.         return card2.rank + 1 == card1.rank
  17811.  
  17812.  
  17813.  
  17814. class Odessa(RussianSolitaire):
  17815.     
  17816.     def startGame(_):
  17817.         for i in range(3):
  17818.             _.s.talon.dealRow(flip = 0, frames = 0)
  17819.         
  17820.         for i in range(2):
  17821.             _.s.talon.dealRow(frames = 0)
  17822.         
  17823.         for i in range(2):
  17824.             _.s.talon.dealRow(rows = _.s.rows[1:6], frames = 0)
  17825.         
  17826.         _.startDealSample()
  17827.         _.s.talon.dealRow()
  17828.         if not __debug__ and len(_.s.talon.cards) == 0:
  17829.             raise AssertionError
  17830.         0
  17831.  
  17832.  
  17833.  
  17834. class Alaska_RowStack(Yukon_SS_RowStack):
  17835.     
  17836.     def _isSequence(_, c1, c2):
  17837.         if not c1.suit == c2.suit and (c1.rank + _.cap.dir) % _.cap.mod == c2.rank:
  17838.             pass
  17839.         return (c2.rank + _.cap.dir) % _.cap.mod == c1.rank
  17840.  
  17841.  
  17842.  
  17843. class Alaska(RussianSolitaire):
  17844.     RowStack_Class = StackWrapper(Alaska_RowStack, base_rank = KING)
  17845.  
  17846.  
  17847. class ChineseDiscipline(Yukon):
  17848.     Layout_Method = Layout.klondikeLayout
  17849.     Talon_Class = DealRowTalonStack
  17850.     
  17851.     def createGame(_):
  17852.         return Yukon.createGame(_, waste = 0, texts = 1)
  17853.  
  17854.     
  17855.     def startGame(_):
  17856.         for i in (3, 3, 3, 4, 5, 6):
  17857.             _.s.talon.dealRow(rows = _.s.rows[:i], flip = 1, frames = 0)
  17858.             _.s.talon.dealRow(rows = _.s.rows[i:], flip = 0, frames = 0)
  17859.         
  17860.         _.startDealSample()
  17861.         _.s.talon.dealRow()
  17862.  
  17863.  
  17864.  
  17865. class ChineseSolitaire(ChineseDiscipline):
  17866.     RowStack_Class = Yukon_AC_RowStack
  17867.  
  17868.  
  17869. class Queenie(Yukon):
  17870.     Layout_Method = Layout.klondikeLayout
  17871.     Talon_Class = DealRowTalonStack
  17872.     
  17873.     def createGame(_):
  17874.         return Yukon.createGame(_, waste = 0, texts = 1)
  17875.  
  17876.     
  17877.     def startGame(_, flip = 1, reverse = 1):
  17878.         for i in range(1, len(_.s.rows)):
  17879.             _.s.talon.dealRow(rows = _.s.rows[i:], flip = flip, frames = 0, reverse = reverse)
  17880.         
  17881.         _.startDealSample()
  17882.         _.s.talon.dealRow(reverse = reverse)
  17883.  
  17884.  
  17885.  
  17886. class Rushdike(RussianSolitaire):
  17887.     Layout_Method = Layout.klondikeLayout
  17888.     Talon_Class = DealRowTalonStack
  17889.     
  17890.     def createGame(_):
  17891.         return RussianSolitaire.createGame(_, waste = 0, texts = 1)
  17892.  
  17893.     
  17894.     def startGame(_, flip = 0, reverse = 1):
  17895.         for i in range(1, len(_.s.rows)):
  17896.             _.s.talon.dealRow(rows = _.s.rows[i:], flip = flip, frames = 0, reverse = reverse)
  17897.         
  17898.         _.startDealSample()
  17899.         _.s.talon.dealRow(reverse = reverse)
  17900.  
  17901.  
  17902.  
  17903. class RussianPoint(Rushdike):
  17904.     
  17905.     def startGame(_):
  17906.         r = _.s.rows
  17907.         for i in (1, 1, 2, 2, 3, 3):
  17908.             _.s.talon.dealRow(rows = r[i:len(r) - i], flip = 0, frames = 0)
  17909.         
  17910.         _.startDealSample()
  17911.         _.s.talon.dealRow()
  17912.  
  17913.  
  17914.  
  17915. class Abacus_Foundation(SS_FoundationStack):
  17916.     
  17917.     def __init__(_, x, y, game, suit, **cap):
  17918.         kwdefault(cap, base_rank = suit, mod = 13, dir = suit + 1, max_move = 0)
  17919.         apply(SS_FoundationStack.__init__, (_, x, y, game, suit), cap)
  17920.  
  17921.  
  17922.  
  17923. class Abacus_RowStack(Yukon_SS_RowStack):
  17924.     
  17925.     def _isSequence(_, c1, c2):
  17926.         (dir, mod) = (-(c1.suit + 1), 13)
  17927.         if c1.suit == c2.suit:
  17928.             pass
  17929.         return (c1.rank + dir) % mod == c2.rank
  17930.  
  17931.  
  17932.  
  17933. class Abacus(Rushdike):
  17934.     Foundation_Class = Abacus_Foundation
  17935.     RowStack_Class = Abacus_RowStack
  17936.     
  17937.     def createGame(_):
  17938.         l = Rushdike.createGame(_)
  17939.         help = 'Club:    A 2 3 4 5 6 7 8 9 T J Q K\n' + 'Spade:   2 4 6 8 T Q A 3 5 7 9 J K\n' + 'Heart:   3 6 9 Q 2 5 8 J A 4 7 T K\n' + 'Diamond: 4 8 Q 3 7 J 2 6 T A 5 9 K'
  17940.         _.texts.help = MfxCanvasText(_.canvas, l.XM, _.height - l.YM, text = help, anchor = 'sw', font = getFont('canvas_fixed'))
  17941.  
  17942.     
  17943.     def _shuffleHook(_, cards):
  17944.         return _._shuffleHookMoveToTop(cards, (lambda c: (c.id in (0, 14, 28, 42), c.suit)))
  17945.  
  17946.     
  17947.     def startGame(_, flip = 1, reverse = 1):
  17948.         _.s.talon.dealRow(rows = _.s.foundations, frames = 0)
  17949.         for i in range(1, len(_.s.rows)):
  17950.             _.s.talon.dealRow(rows = _.s.rows[i:], flip = flip, frames = 0, reverse = reverse)
  17951.         
  17952.         _.startDealSample()
  17953.         _.s.talon.dealRow(reverse = reverse)
  17954.  
  17955.     
  17956.     def shallHighlightMatch(_, stack1, card1, stack2, card2):
  17957.         (dir, mod) = (-(card1.suit + 1), 13)
  17958.         if not card1.suit == card2.suit and (card1.rank + dir) % mod == card2.rank:
  17959.             pass
  17960.         return (card2.rank + dir) % mod == card1.rank
  17961.  
  17962.  
  17963. registerGame(GameInfo(19, Yukon, 'Yukon', GI.GT_YUKON, 1, 0))
  17964. registerGame(GameInfo(20, RussianSolitaire, 'Russian Solitaire', GI.GT_YUKON, 1, 0))
  17965. registerGame(GameInfo(27, Odessa, 'Odessa', GI.GT_YUKON, 1, 0))
  17966. registerGame(GameInfo(186, Alaska, 'Alaska', GI.GT_YUKON, 1, 0))
  17967.  
  17968. class CastlesInSpain(Game):
  17969.     Layout_Method = Layout.bakersDozenLayout
  17970.     Talon_Class = InitialDealTalonStack
  17971.     Foundation_Class = SS_FoundationStack
  17972.     RowStack_Class = AC_RowStack
  17973.     Hint_Class = CautiousDefaultHint
  17974.     
  17975.     def createGame(_, **layout):
  17976.         (l, s) = (Layout(_), _.s)
  17977.         kwdefault(layout, rows = 13, playcards = 9)
  17978.         apply(_.Layout_Method, (l,), layout)
  17979.         _.setSize(l.size[0], l.size[1])
  17980.         s.talon = _.Talon_Class(l.s.talon.x, l.s.talon.y, _)
  17981.         for r in l.s.foundations:
  17982.             s.foundations.append(_.Foundation_Class(r.x, r.y, _, r.suit))
  17983.         
  17984.         for r in l.s.rows:
  17985.             s.rows.append(_.RowStack_Class(r.x, r.y, _, max_move = 1, max_accept = 1))
  17986.         
  17987.         l.defaultAll()
  17988.  
  17989.     
  17990.     def startGame(_, flip = (0, 0, 0)):
  17991.         for f in flip:
  17992.             _.s.talon.dealRow(flip = f, frames = 0)
  17993.         
  17994.         _.startDealSample()
  17995.         _.s.talon.dealRow()
  17996.  
  17997.  
  17998.  
  17999. class Martha_RowStack(AC_RowStack):
  18000.     
  18001.     def acceptsCards(_, from_stack, cards):
  18002.         if not AC_RowStack.acceptsCards(_, from_stack, cards):
  18003.             return 0
  18004.         
  18005.         if not _.cards:
  18006.             pass
  18007.         return len(cards) == 1
  18008.  
  18009.  
  18010.  
  18011. class Martha(CastlesInSpain):
  18012.     RowStack_Class = FullStackWrapper(Martha_RowStack)
  18013.     
  18014.     def createGame(_):
  18015.         CastlesInSpain.createGame(_, rows = 12, playcards = 13)
  18016.  
  18017.     
  18018.     def _shuffleHook(_, cards):
  18019.         return _._shuffleHookMoveToBottom(cards, (lambda c: (c.rank == 0, c.suit)))
  18020.  
  18021.     
  18022.     def startGame(_):
  18023.         CastlesInSpain.startGame(_, flip = (0, 1, 0))
  18024.         _.s.talon.dealRow(rows = _.s.foundations)
  18025.  
  18026.  
  18027.  
  18028. class BakersDozen(CastlesInSpain):
  18029.     RowStack_Class = StackWrapper(RK_RowStack, base_rank = NO_RANK)
  18030.     
  18031.     def _shuffleHook(_, cards):
  18032.         (i, n) = (0, len(_.s.rows))
  18033.         kings = []
  18034.         for c in cards:
  18035.             i = i + 1
  18036.         
  18037.         for i in kings:
  18038.             j = i % n
  18039.             while j < i:
  18040.                 j = j + n
  18041.                 continue
  18042.                 None if c.rank == KING else cards if cards[j].rank != KING else kings
  18043.         
  18044.         cards.reverse()
  18045.         return cards
  18046.  
  18047.     
  18048.     def startGame(_):
  18049.         CastlesInSpain.startGame(_, flip = (1, 1, 1))
  18050.  
  18051.     
  18052.     def shallHighlightMatch(_, stack1, card1, stack2, card2):
  18053.         if not card1.rank + 1 == card2.rank:
  18054.             pass
  18055.         return card2.rank + 1 == card1.rank
  18056.  
  18057.  
  18058.  
  18059. class SpanishPatience(BakersDozen):
  18060.     Foundation_Class = AC_FoundationStack
  18061.  
  18062.  
  18063. class GoodMeasure(BakersDozen):
  18064.     
  18065.     def createGame(_):
  18066.         CastlesInSpain.createGame(_, rows = 10)
  18067.  
  18068.     
  18069.     def _shuffleHook(_, cards):
  18070.         cards = BakersDozen._shuffleHook(_, cards)
  18071.         return _._shuffleHookMoveToBottom(cards, (lambda c: (c.rank == 0, c.suit)), 2)
  18072.  
  18073.     
  18074.     def startGame(_):
  18075.         CastlesInSpain.startGame(_, flip = (1, 1, 1, 1))
  18076.         for i in range(2):
  18077.             c = _.s.talon.cards[-1]
  18078.             if not __debug__ and c.rank == ACE:
  18079.                 raise AssertionError
  18080.             0
  18081.             _.flipMove(_.s.talon)
  18082.             _.moveMove(1, _.s.talon, _.s.foundations[c.suit])
  18083.         
  18084.         if not __debug__ and len(_.s.talon.cards) == 0:
  18085.             raise AssertionError
  18086.         range(2)
  18087.  
  18088.  
  18089.  
  18090. class Cruel_Talon(TalonStack):
  18091.     
  18092.     def canDealCards(_):
  18093.         if _.game.demo and _.game.moves.index >= 100:
  18094.             return 0
  18095.         
  18096.         return not _.game.isGameWon()
  18097.  
  18098.     
  18099.     def dealCards(_, sound = 0):
  18100.         lr = len(_.game.s.rows)
  18101.         num_cards = 0
  18102.         if not __debug__ and len(_.cards) == 0:
  18103.             raise AssertionError
  18104.         rows = list(_.game.s.rows)[:]
  18105.         rows.reverse()
  18106.         for r in rows:
  18107.             for i in range(len(r.cards)):
  18108.                 num_cards = num_cards + 1
  18109.                 _.game.moveMove(1, r, _, frames = 0)
  18110.             
  18111.         
  18112.         if not __debug__ and len(_.cards) == num_cards:
  18113.             raise AssertionError
  18114.         0
  18115.         _.game.nextRoundMove(_)
  18116.         (n, i) = (num_cards, 0)
  18117.         deal = [
  18118.             4] * lr
  18119.         extra_cards = n - 4 * lr
  18120.         while extra_cards > 0:
  18121.             deal[i] = deal[i] + 1
  18122.             i = (i + 1) % lr
  18123.             extra_cards = extra_cards - 1
  18124.             continue
  18125.             rows if num_cards == 0 else 0
  18126.         for i in range(lr):
  18127.             k = min(deal[i], n)
  18128.             frames = (0, 4)[n <= 3 * 4]
  18129.             for j in range(k):
  18130.                 _.game.moveMove(1, _, _.game.s.rows[i], frames = frames)
  18131.             
  18132.             n = n - k
  18133.         
  18134.         if __debug__:
  18135.             if len(_.cards) == len(_.cards):
  18136.                 pass
  18137.             elif not len(_.cards) == 0:
  18138.                 raise AssertionError
  18139.         0 if n == 0 else range(k)
  18140.         return num_cards
  18141.  
  18142.  
  18143.  
  18144. class Cruel(CastlesInSpain):
  18145.     Talon_Class = StackWrapper(Cruel_Talon, max_rounds = -1)
  18146.     RowStack_Class = StackWrapper(SS_RowStack, base_rank = NO_RANK)
  18147.     
  18148.     def createGame(_):
  18149.         CastlesInSpain.createGame(_, rows = 12)
  18150.  
  18151.     
  18152.     def _shuffleHook(_, cards):
  18153.         return _._shuffleHookMoveToBottom(cards, (lambda c: (c.rank == 0, c.suit)))
  18154.  
  18155.     
  18156.     def startGame(_):
  18157.         CastlesInSpain.startGame(_, flip = (1, 1, 1))
  18158.         _.s.talon.dealRow(rows = _.s.foundations)
  18159.  
  18160.  
  18161. registerGame(GameInfo(83, CastlesInSpain, 'Castles in Spain', GI.GT_BAKERS_DOZEN, 1, 0))
  18162. registerGame(GameInfo(84, Martha, 'Martha', GI.GT_BAKERS_DOZEN, 1, 0))
  18163. registerGame(GameInfo(31, BakersDozen, "Baker's Dozen", GI.GT_BAKERS_DOZEN, 1, 0))
  18164. registerGame(GameInfo(85, SpanishPatience, 'Spanish Patience', GI.GT_BAKERS_DOZEN, 1, 0))
  18165. registerGame(GameInfo(86, GoodMeasure, 'Good Measure', GI.GT_BAKERS_DOZEN, 1, 0))
  18166. registerGame(GameInfo(104, Cruel, 'Cruel', GI.GT_BAKERS_DOZEN, 1, -1))
  18167.  
  18168. class Fan_Hint(CautiousDefaultHint):
  18169.     pass
  18170.  
  18171.  
  18172. class Fan(Game):
  18173.     Talon_Class = InitialDealTalonStack
  18174.     Foundation_Class = SS_FoundationStack
  18175.     RowStack_Class = KingSS_RowStack
  18176.     Hint_Class = Fan_Hint
  18177.     
  18178.     def createGame(_, rows = (5, 5, 5, 3), playcards = 9):
  18179.         (l, s) = (Layout(_, XOFFSET = 10), _.s)
  18180.         w = max(2 * l.XS, l.XS + (playcards - 1) * l.XOFFSET)
  18181.         w = min(3 * l.XS, w)
  18182.         w = w + 1 & ~1
  18183.         _.setSize(l.XM + max(rows) * w, l.YM + (1 + len(rows)) * l.YS)
  18184.         (x, y) = (l.XM + w, l.YM)
  18185.         for j in range(_.gameinfo.decks):
  18186.             for i in range(4):
  18187.                 s.foundations.append(_.Foundation_Class(x, y, _, suit = i))
  18188.                 x = x + w / _.gameinfo.decks
  18189.             
  18190.         
  18191.         for i in range(len(rows)):
  18192.             (x, y) = (l.XM, y + l.YS)
  18193.             for j in range(rows[i]):
  18194.                 stack = _.RowStack_Class(x, y, _, max_move = 1, max_accept = 1)
  18195.                 (stack.CARD_XOFFSET, stack.CARD_YOFFSET) = (l.XOFFSET, 0)
  18196.                 s.rows.append(stack)
  18197.                 x = x + w
  18198.             
  18199.         
  18200.         (x, y) = (_.width - l.XS, _.height - l.YS)
  18201.         s.talon = _.Talon_Class(x, y, _)
  18202.         l.defaultStackGroups()
  18203.         return l
  18204.  
  18205.     
  18206.     def startGame(_):
  18207.         for i in range(2):
  18208.             _.s.talon.dealRow(rows = _.s.rows[:17], frames = 0)
  18209.         
  18210.         _.startDealSample()
  18211.         _.s.talon.dealRow()
  18212.         if not __debug__ and len(_.s.talon.cards) == 0:
  18213.             raise AssertionError
  18214.         0
  18215.  
  18216.     
  18217.     def shallHighlightMatch(_, stack1, card1, stack2, card2):
  18218.         if not card1.suit == card2.suit and card1.rank + 1 == card2.rank:
  18219.             pass
  18220.         return card2.rank + 1 == card1.rank
  18221.  
  18222.     
  18223.     def getHighlightPilesStacks(_):
  18224.         return ()
  18225.  
  18226.  
  18227.  
  18228. class ScotchPatience(Fan):
  18229.     Foundation_Class = AC_FoundationStack
  18230.     RowStack_Class = StackWrapper(RK_RowStack, base_rank = NO_RANK)
  18231.  
  18232.  
  18233. class Shamrocks_RowStack(BasicRowStack):
  18234.     
  18235.     def acceptsCards(_, from_stack, cards):
  18236.         if not BasicRowStack.acceptsCards(_, from_stack, cards):
  18237.             return 0
  18238.         
  18239.         (c1, c2) = (_.cards[-1], cards[0])
  18240.         if not c1.rank == (c2.rank + 1) % _.cap.mod:
  18241.             pass
  18242.         return c2.rank == (c1.rank + 1) % _.cap.mod
  18243.  
  18244.  
  18245.  
  18246. class Shamrocks(Fan):
  18247.     RowStack_Class = StackWrapper(Shamrocks_RowStack, base_rank = NO_RANK, max_cards = 3)
  18248.  
  18249.  
  18250. class LaBelleLucie_Talon(TalonStack):
  18251.     
  18252.     def canDealCards(_):
  18253.         if _.round != _.max_rounds:
  18254.             pass
  18255.         return not _.game.isGameWon()
  18256.  
  18257.     
  18258.     def dealCards(_, sound = 0):
  18259.         n = _.redealCards1()
  18260.         if n == 0:
  18261.             return 0
  18262.         
  18263.         _.redealCards2()
  18264.         if sound:
  18265.             _.game.startDealSample()
  18266.         
  18267.         _.redealCards3()
  18268.         if sound:
  18269.             _.game.stopSamples()
  18270.         
  18271.         return n
  18272.  
  18273.     
  18274.     def redealCards1(_):
  18275.         if not __debug__ and len(_.cards) == 0:
  18276.             raise AssertionError
  18277.         num_cards = 0
  18278.         for r in _.game.s.rows:
  18279.             pass
  18280.         
  18281.         if not __debug__ and len(_.cards) == num_cards:
  18282.             raise AssertionError
  18283.         None if r.cards else _.game.s.rows
  18284.         return num_cards
  18285.  
  18286.     
  18287.     def redealCards2(_):
  18288.         if not __debug__ and _.round != _.max_rounds:
  18289.             raise AssertionError
  18290.         if not __debug__ and _.cards:
  18291.             raise AssertionError
  18292.         _.game.shuffleStackMove(_)
  18293.         _.game.nextRoundMove(_)
  18294.  
  18295.     
  18296.     def redealCards3(_, face_up = 1):
  18297.         to_stacks = _.game.s.rows
  18298.         n = min(len(_.cards), 3 * len(to_stacks))
  18299.         for i in range(3):
  18300.             j = (n / 3, (n + 1) / 3, (n + 2) / 3)[i]
  18301.             frames = (0, 0, 4)[i]
  18302.             for r in to_stacks[:j]:
  18303.                 _.game.moveMove(1, _, r, frames = frames)
  18304.             
  18305.         
  18306.  
  18307.  
  18308.  
  18309. class LaBelleLucie(Fan):
  18310.     Talon_Class = StackWrapper(LaBelleLucie_Talon, max_rounds = 3)
  18311.     RowStack_Class = StackWrapper(SS_RowStack, base_rank = NO_RANK)
  18312.  
  18313.  
  18314. class SuperFlowerGarden(LaBelleLucie):
  18315.     RowStack_Class = StackWrapper(RK_RowStack, base_rank = NO_RANK)
  18316.  
  18317.  
  18318. class ThreeShufflesAndADraw_RowStack(SS_RowStack):
  18319.     
  18320.     def moveMove(_, ncards, to_stack, frames = -1, shadow = -1):
  18321.         (game, r) = (_.game, _.game.s.reserves[0])
  18322.         if to_stack is not r:
  18323.             SS_RowStack.moveMove(_, ncards, to_stack, frames = frames, shadow = shadow)
  18324.             return None
  18325.         
  18326.         f = _._canDrawCard()
  18327.         if __debug__:
  18328.             if not f and game.draw_done == 0 and ncards == 1:
  18329.                 raise AssertionError
  18330.         game.updateStackMove(r, 2 | 16)
  18331.         game.moveMove(1, _, r, frames = frames, shadow = shadow)
  18332.         game.updateStackMove(r, 3 | 64)
  18333.         game.updateStackMove(r, 1 | 16)
  18334.         if 1 or not (game.demo):
  18335.             game.playSample('drop', priority = 200)
  18336.         
  18337.         if frames == 0:
  18338.             frames = -1
  18339.         
  18340.         game.moveMove(1, _, f, frames = frames, shadow = shadow)
  18341.         old_state = game.enterState(game.S_FILL)
  18342.         game.moveMove(1, r, _, frames = frames, shadow = shadow)
  18343.         game.leaveState(old_state)
  18344.  
  18345.     
  18346.     def _canDrawCard(_):
  18347.         if len(_.cards) >= 2:
  18348.             pile = _.cards[-2:-1]
  18349.             for s in _.game.s.foundations + _.game.s.rows:
  18350.                 pass
  18351.             
  18352.         
  18353.         return None
  18354.  
  18355.  
  18356.  
  18357. class ThreeShufflesAndADraw_ReserveStack(ReserveStack):
  18358.     
  18359.     def acceptsCards(_, from_stack, cards):
  18360.         if not ReserveStack.acceptsCards(_, from_stack, cards):
  18361.             return 0
  18362.         
  18363.         if _.game.draw_done or not from_stack._canDrawCard():
  18364.             return 0
  18365.         
  18366.         return 1
  18367.  
  18368.     
  18369.     def updateModel(_, undo, flags):
  18370.         if not __debug__ and undo == _.game.draw_done:
  18371.             raise AssertionError
  18372.         _.game.draw_done = not (_.game.draw_done)
  18373.  
  18374.     
  18375.     def updateText(_):
  18376.         if _.game.preview > 1 or _.texts.misc is None:
  18377.             return None
  18378.         
  18379.         t = ('X', 'Draw')[_.game.draw_done == 0]
  18380.         _.texts.misc.config(text = t)
  18381.  
  18382.     
  18383.     def prepareView(_):
  18384.         ReserveStack.prepareView(_)
  18385.         if not (_.is_visible) or _.game.preview > 1:
  18386.             return None
  18387.         
  18388.         images = _.game.app.images
  18389.         (x, y) = (_.x + images.CARDW / 2, _.y + images.CARDH / 2)
  18390.         _.texts.misc = MfxCanvasText(_.game.canvas, x, y, anchor = 'center')
  18391.  
  18392.  
  18393.  
  18394. class ThreeShufflesAndADraw(LaBelleLucie):
  18395.     RowStack_Class = StackWrapper(ThreeShufflesAndADraw_RowStack, base_rank = NO_RANK)
  18396.     
  18397.     def createGame(_):
  18398.         l = LaBelleLucie.createGame(_)
  18399.         s = _.s
  18400.         (x, y) = (s.rows[3].x, s.rows[-1].y)
  18401.         s.reserves.append(ThreeShufflesAndADraw_ReserveStack(x, y, _))
  18402.         l.defaultStackGroups()
  18403.         _.draw_done = 0
  18404.  
  18405.     
  18406.     def startGame(_):
  18407.         _.draw_done = 0
  18408.         _.s.reserves[0].updateText()
  18409.         LaBelleLucie.startGame(_)
  18410.  
  18411.     
  18412.     def _restoreGameHook(_, game):
  18413.         _.draw_done = game.loadinfo.draw_done
  18414.  
  18415.     
  18416.     def _loadGameHook(_, p):
  18417.         _.loadinfo.addattr(draw_done = p.load())
  18418.  
  18419.     
  18420.     def _saveGameHook(_, p):
  18421.         p.dump(_.draw_done)
  18422.  
  18423.  
  18424.  
  18425. class Trefoil(LaBelleLucie):
  18426.     GAME_VERSION = 2
  18427.     Foundation_Class = StackWrapper(SS_FoundationStack, min_cards = 1)
  18428.     
  18429.     def createGame(_):
  18430.         return Fan.createGame(_, rows = (5, 5, 5, 1))
  18431.  
  18432.     
  18433.     def _shuffleHook(_, cards):
  18434.         return _._shuffleHookMoveToBottom(cards, (lambda c: (c.rank == 0, c.suit)))
  18435.  
  18436.     
  18437.     def startGame(_):
  18438.         for i in range(2):
  18439.             _.s.talon.dealRow(frames = 0)
  18440.         
  18441.         _.startDealSample()
  18442.         _.s.talon.dealRow()
  18443.         _.s.talon.dealRow(rows = _.s.foundations)
  18444.  
  18445.  
  18446.  
  18447. class Intelligence_Talon(LaBelleLucie_Talon):
  18448.     dealToStacks = TalonStack.dealToStacksOrFoundations
  18449.     
  18450.     def redealCards1(_):
  18451.         if not __debug__ and len(_.cards) == 0:
  18452.             raise AssertionError
  18453.         r = _.game.s.reserves[0]
  18454.         num_cards = len(r.cards)
  18455.         _.game.moveMove(len(r.cards), r, _, frames = 0)
  18456.         for r in _.game.s.rows:
  18457.             num_cards = num_cards + len(r.cards)
  18458.             while r.cards:
  18459.                 _.game.moveMove(1, r, _, frames = 0)
  18460.                 _.game.flipMove(_)
  18461.                 continue
  18462.                 0
  18463.         
  18464.         if not __debug__ and len(_.cards) == num_cards:
  18465.             raise AssertionError
  18466.         _.game.s.rows
  18467.         if not __debug__ and forall((lambda c: not (c.face_up)), _.cards):
  18468.             raise AssertionError
  18469.         return num_cards
  18470.  
  18471.     
  18472.     def redealCards3(_, face_up = 1):
  18473.         for r in _.game.s.rows:
  18474.             while len(r.cards) < 3:
  18475.                 _.dealToStacks([
  18476.                     r], frames = 4)
  18477.                 continue
  18478.                 None if not (_.cards) else _.game.s.rows
  18479.         
  18480.         _.game.moveMove(len(_.cards), _, _.game.s.reserves[0], frames = 0)
  18481.  
  18482.  
  18483.  
  18484. class Intelligence_RowStack(BasicRowStack):
  18485.     
  18486.     def acceptsCards(_, from_stack, cards):
  18487.         if not BasicRowStack.acceptsCards(_, from_stack, cards):
  18488.             return 0
  18489.         
  18490.         if not __debug__ and _.cards:
  18491.             raise AssertionError
  18492.         (c1, c2) = (_.cards[-1], cards[0])
  18493.         if c1.suit != c2.suit:
  18494.             return 0
  18495.         
  18496.         if not c1.rank == (c2.rank + 1) % _.cap.mod:
  18497.             pass
  18498.         return c2.rank == (c1.rank + 1) % _.cap.mod
  18499.  
  18500.     
  18501.     def fillStack(_):
  18502.         if not (_.cards):
  18503.             r = _.game.s.reserves[0]
  18504.             if r.cards:
  18505.                 r.dealRow(rows = (_, _, _), sound = 1)
  18506.             
  18507.         
  18508.  
  18509.  
  18510.  
  18511. class Intelligence_ReserveStack(ReserveStack, DealRow_StackMethods):
  18512.     
  18513.     def canFlipCard(_):
  18514.         return 0
  18515.  
  18516.     dealToStacks = TalonStack.dealToStacksOrFoundations
  18517.  
  18518.  
  18519. class Intelligence(Fan):
  18520.     Talon_Class = StackWrapper(Intelligence_Talon, max_rounds = 3)
  18521.     RowStack_Class = StackWrapper(Intelligence_RowStack, base_rank = NO_RANK)
  18522.     
  18523.     def createGame(_):
  18524.         l = Fan.createGame(_)
  18525.         s = _.s
  18526.         (x, y) = (s.talon.x - l.XS, s.talon.y)
  18527.         s.reserves.append(Intelligence_ReserveStack(x, y, _, max_move = 0, max_accept = 0, max_cards = 999999))
  18528.         l.createText(s.reserves[0], 'sw')
  18529.         l.defaultStackGroups()
  18530.  
  18531.     
  18532.     def startGame(_):
  18533.         talon = _.s.talon
  18534.         for i in range(2):
  18535.             talon.dealRow(frames = 0)
  18536.         
  18537.         _.startDealSample()
  18538.         talon.dealRow()
  18539.         _.moveMove(len(talon.cards), talon, _.s.reserves[0], frames = 0)
  18540.  
  18541.  
  18542.  
  18543. class BlackHole_Foundation(AbstractFoundationStack):
  18544.     
  18545.     def acceptsCards(_, from_stack, cards):
  18546.         if not AbstractFoundationStack.acceptsCards(_, from_stack, cards):
  18547.             return 0
  18548.         
  18549.         if _.cards:
  18550.             (r1, r2) = (_.cards[-1].rank, cards[0].rank)
  18551.             if not (r1 + 1) % _.cap.mod == r2:
  18552.                 pass
  18553.             return (r2 + 1) % _.cap.mod == r1
  18554.         
  18555.         return 1
  18556.  
  18557.  
  18558.  
  18559. class BlackHole_RowStack(ReserveStack):
  18560.     
  18561.     def clickHandler(_, event):
  18562.         return _.doubleclickHandler(event)
  18563.  
  18564.  
  18565.  
  18566. class BlackHole(Game):
  18567.     RowStack_Class = StackWrapper(BlackHole_RowStack, max_accept = 0, max_cards = 3)
  18568.     Hint_Class = Fan_Hint
  18569.     
  18570.     def createGame(_, playcards = 5):
  18571.         (l, s) = (Layout(_, XOFFSET = 12), _.s)
  18572.         w = max(2 * l.XS, l.XS + (playcards - 1) * l.XOFFSET)
  18573.         _.setSize(l.XM + 5 * w, l.YM + 4 * l.YS)
  18574.         y = l.YM
  18575.         for i in range(5):
  18576.             x = l.XM + i * w
  18577.             s.rows.append(_.RowStack_Class(x, y, _))
  18578.         
  18579.         for i in range(2):
  18580.             y = y + l.YS
  18581.             for j in (0, 1, 3, 4):
  18582.                 x = l.XM + j * w
  18583.                 s.rows.append(_.RowStack_Class(x, y, _))
  18584.             
  18585.         
  18586.         y = y + l.YS
  18587.         for i in range(4):
  18588.             x = l.XM + i * w
  18589.             s.rows.append(_.RowStack_Class(x, y, _))
  18590.         
  18591.         for r in s.rows:
  18592.             r.CARD_XOFFSET = l.XOFFSET
  18593.             r.CARD_YOFFSET = 0
  18594.         
  18595.         (x, y) = (l.XM + 2 * w, l.YM + 3 * l.YS / 2)
  18596.         s.foundations.append(BlackHole_Foundation(x, y, _, ANY_SUIT, dir = 0, mod = 13, max_move = 0, max_cards = 52))
  18597.         l.createText(s.foundations[0], 'ss')
  18598.         (x, y) = (l.XM + 4 * w, _.height - l.YS)
  18599.         s.talon = InitialDealTalonStack(x, y, _)
  18600.         l.defaultStackGroups()
  18601.  
  18602.     
  18603.     def _shuffleHook(_, cards):
  18604.         return _._shuffleHookMoveToBottom(cards, (lambda c: (c.id == 13, c.suit)), 1)
  18605.  
  18606.     
  18607.     def startGame(_):
  18608.         for i in range(2):
  18609.             _.s.talon.dealRow(frames = 0)
  18610.         
  18611.         _.startDealSample()
  18612.         _.s.talon.dealRow()
  18613.         _.s.talon.dealRow(rows = _.s.foundations)
  18614.  
  18615.     
  18616.     def getAutoStacks(_, event = None):
  18617.         if event is None:
  18618.             return ((), (), _.sg.dropstacks)
  18619.         else:
  18620.             return ((), _.sg.dropstacks, _.sg.dropstacks)
  18621.  
  18622.  
  18623. registerGame(GameInfo(56, Fan, 'Fan', GI.GT_FAN_TYPE | GI.GT_OPEN, 1, 0, altnames = 'Midnight Oil'))
  18624. registerGame(GameInfo(87, ScotchPatience, 'Scotch Patience', GI.GT_FAN_TYPE | GI.GT_OPEN, 1, 0))
  18625. registerGame(GameInfo(57, Shamrocks, 'Shamrocks', GI.GT_FAN_TYPE | GI.GT_OPEN, 1, 0))
  18626. registerGame(GameInfo(901, LaBelleLucie, 'La Belle Lucie', GI.GT_FAN_TYPE | GI.GT_OPEN, 1, 2))
  18627. registerGame(GameInfo(132, SuperFlowerGarden, 'Super Flower Garden', GI.GT_FAN_TYPE | GI.GT_OPEN, 1, 2))
  18628. registerGame(GameInfo(128, ThreeShufflesAndADraw, 'Three Shuffles and a Draw', GI.GT_FAN_TYPE | GI.GT_OPEN, 1, 2))
  18629. registerGame(GameInfo(88, Trefoil, 'Trefoil', GI.GT_FAN_TYPE | GI.GT_OPEN, 1, 2))
  18630. registerGame(GameInfo(227, Intelligence, 'Intelligence', GI.GT_FAN_TYPE, 2, 2))
  18631. registerGame(GameInfo(98, BlackHole, 'Black Hole', GI.GT_FAN_TYPE | GI.GT_OPEN, 1, 0))
  18632.  
  18633. class BeleagueredCastleType_Hint(CautiousDefaultHint):
  18634.     pass
  18635.  
  18636.  
  18637. class StreetsAndAlleys(Game):
  18638.     Hint_Class = BeleagueredCastleType_Hint
  18639.     
  18640.     def createGame(_, playcards = 13):
  18641.         (l, s) = (Layout(_, XOFFSET = 12), _.s)
  18642.         w = max(3 * l.XS, l.XS + (playcards - 1) * l.XOFFSET)
  18643.         x0 = l.XM
  18644.         x1 = x0 + w + 2 * l.XM
  18645.         x2 = x1 + l.XS + 2 * l.XM
  18646.         x3 = x2 + w + l.XM
  18647.         _.setSize(x3, l.YM + 4 * l.YS)
  18648.         (x, y) = (x1, l.YM)
  18649.         for i in range(4):
  18650.             s.foundations.append(SS_FoundationStack(x, y, _, i, max_move = 0))
  18651.             y = y + l.YS
  18652.         
  18653.         for x, y in ((x0, l.YM), (x2, l.YM)):
  18654.             for i in range(4):
  18655.                 stack = RK_RowStack(x, y, _, max_move = 1, max_accept = 1)
  18656.                 (stack.CARD_XOFFSET, stack.CARD_YOFFSET) = (l.XOFFSET, 0)
  18657.                 s.rows.append(stack)
  18658.                 y = y + l.YS
  18659.             
  18660.         
  18661.         (x, y) = (_.width - l.XS, _.height - l.YS)
  18662.         s.talon = InitialDealTalonStack(x, y, _)
  18663.         l.defaultStackGroups()
  18664.  
  18665.     
  18666.     def startGame(_):
  18667.         for i in range(4):
  18668.             _.s.talon.dealRow(frames = 0)
  18669.         
  18670.         _.startDealSample()
  18671.         for i in range(3):
  18672.             _.s.talon.dealRowAvail()
  18673.         
  18674.         if not __debug__ and len(_.s.talon.cards) == 0:
  18675.             raise AssertionError
  18676.         0
  18677.  
  18678.  
  18679.  
  18680. class BeleagueredCastle(StreetsAndAlleys):
  18681.     
  18682.     def _shuffleHook(_, cards):
  18683.         return _._shuffleHookMoveToBottom(cards, (lambda c: (c.rank == 0, c.suit)))
  18684.  
  18685.     
  18686.     def startGame(_):
  18687.         for i in range(4):
  18688.             _.s.talon.dealRow(frames = 0)
  18689.         
  18690.         _.startDealSample()
  18691.         for i in range(2):
  18692.             _.s.talon.dealRow()
  18693.         
  18694.         _.s.talon.dealRow(rows = _.s.foundations)
  18695.         if not __debug__ and len(_.s.talon.cards) == 0:
  18696.             raise AssertionError
  18697.         0
  18698.  
  18699.  
  18700.  
  18701. class Citadel(StreetsAndAlleys):
  18702.     
  18703.     def _shuffleHook(_, cards):
  18704.         return _._shuffleHookMoveToTop(cards, (lambda c: (c.rank == 0, c.suit)))
  18705.  
  18706.     
  18707.     def startGame(_):
  18708.         frames = 4
  18709.         talon = _.s.talon
  18710.         _.startDealSample()
  18711.         talon.dealRow(rows = _.s.foundations, frames = frames)
  18712.         while talon.cards:
  18713.             for r in _.s.rows:
  18714.                 _.flipMove(talon)
  18715.                 for s in _.s.foundations:
  18716.                     pass
  18717.                 
  18718.             
  18719.             continue
  18720.             None if not (talon.cards) else _.s.rows
  18721.  
  18722.  
  18723.  
  18724. class Fortress_RowStack(BasicRowStack):
  18725.     
  18726.     def acceptsCards(_, from_stack, cards):
  18727.         if not BasicRowStack.acceptsCards(_, from_stack, cards):
  18728.             return 0
  18729.         
  18730.         if _.cards:
  18731.             if _.cards[-1].suit != cards[0].suit:
  18732.                 return 0
  18733.             
  18734.             (r1, r2) = (_.cards[-1].rank, cards[0].rank)
  18735.             if not (r1 + 1) % _.cap.mod == r2:
  18736.                 pass
  18737.             return (r2 + 1) % _.cap.mod == r1
  18738.         
  18739.         return 1
  18740.  
  18741.  
  18742.  
  18743. class Fortress(Game):
  18744.     Layout_Method = Layout.klondikeLayout
  18745.     Talon_Class = InitialDealTalonStack
  18746.     Foundation_Class = SS_FoundationStack
  18747.     RowStack_Class = StackWrapper(Fortress_RowStack, max_accept = 1)
  18748.     Hint_Class = BeleagueredCastleType_Hint
  18749.     
  18750.     def createGame(_, **layout):
  18751.         (l, s) = (Layout(_), _.s)
  18752.         kwdefault(layout, rows = 10, waste = 0, texts = 0, playcards = 16)
  18753.         apply(_.Layout_Method, (l,), layout)
  18754.         _.setSize(l.size[0], l.size[1])
  18755.         s.talon = _.Talon_Class(l.s.talon.x, l.s.talon.y, _)
  18756.         if l.s.waste:
  18757.             s.waste = WasteStack(l.s.waste.x, l.s.waste.y, _)
  18758.         
  18759.         for r in l.s.foundations:
  18760.             s.foundations.append(_.Foundation_Class(r.x, r.y, _, suit = r.suit))
  18761.         
  18762.         for r in l.s.rows:
  18763.             s.rows.append(_.RowStack_Class(r.x, r.y, _))
  18764.         
  18765.         l.defaultAll()
  18766.         return l
  18767.  
  18768.     
  18769.     def startGame(_):
  18770.         for i in range(3):
  18771.             _.s.talon.dealRow(frames = 0)
  18772.         
  18773.         _.startDealSample()
  18774.         for i in range(3):
  18775.             _.s.talon.dealRowAvail()
  18776.         
  18777.         if not __debug__ and len(_.s.talon.cards) == 0:
  18778.             raise AssertionError
  18779.         0
  18780.  
  18781.  
  18782.  
  18783. class Chessboard_Foundation(SS_FoundationStack):
  18784.     
  18785.     def __init__(_, x, y, game, suit, **cap):
  18786.         kwdefault(cap, mod = 13, min_cards = 1, max_move = 0)
  18787.         apply(SS_FoundationStack.__init__, (_, x, y, game, suit), cap)
  18788.  
  18789.     
  18790.     def acceptsCards(_, from_stack, cards):
  18791.         if not (_.cards):
  18792.             if len(cards) != 1 or not (cards[0].face_up):
  18793.                 return 0
  18794.             
  18795.             if cards[0].suit != _.cap.base_suit:
  18796.                 return 0
  18797.             
  18798.             for s in _.game.s.foundations:
  18799.                 pass
  18800.             
  18801.             return 1
  18802.         
  18803.         return SS_FoundationStack.acceptsCards(_, from_stack, cards)
  18804.  
  18805.  
  18806.  
  18807. class Chessboard_RowStack(Fortress_RowStack):
  18808.     
  18809.     def canDropCards(_, stacks):
  18810.         if _.game.demo:
  18811.             return Fortress_RowStack.canDropCards(_, stacks)
  18812.         
  18813.         for s in _.game.s.foundations:
  18814.             pass
  18815.         
  18816.         return (None, 0)
  18817.  
  18818.  
  18819.  
  18820. class Chessboard(Fortress):
  18821.     Foundation_Class = Chessboard_Foundation
  18822.     RowStack_Class = StackWrapper(Chessboard_RowStack, max_accept = 1, mod = 13)
  18823.     
  18824.     def createGame(_):
  18825.         l = Fortress.createGame(_)
  18826.         (tx, ty, ta, tf) = l.getTextAttr(_.s.foundations[-1], 'e')
  18827.         font = getFont('canvas_card', cardw = l.CW)
  18828.         _.texts.info = MfxCanvasText(_.canvas, tx + l.XM, ty, anchor = ta, font = font)
  18829.  
  18830.     
  18831.     def updateText(_):
  18832.         if _.preview > 1:
  18833.             return None
  18834.         
  18835.         t = ''
  18836.         for s in _.s.foundations:
  18837.             pass
  18838.         
  18839.         _.texts.info.config(text = t)
  18840.  
  18841.  
  18842. registerGame(GameInfo(146, StreetsAndAlleys, 'Streets and Alleys', GI.GT_BELEAGUERED_CASTLE | GI.GT_OPEN, 1, 0))
  18843. registerGame(GameInfo(34, BeleagueredCastle, 'Beleaguered Castle', GI.GT_BELEAGUERED_CASTLE | GI.GT_OPEN, 1, 0))
  18844. registerGame(GameInfo(145, Citadel, 'Citadel', GI.GT_BELEAGUERED_CASTLE | GI.GT_OPEN, 1, 0))
  18845. registerGame(GameInfo(147, Fortress, 'Fortress', GI.GT_BELEAGUERED_CASTLE | GI.GT_OPEN, 1, 0))
  18846. registerGame(GameInfo(148, Chessboard, 'Chessboard', GI.GT_BELEAGUERED_CASTLE | GI.GT_OPEN, 1, 0))
  18847.  
  18848. class UnionSquare_Foundation(AbstractFoundationStack):
  18849.     
  18850.     def acceptsCards(_, from_stack, cards):
  18851.         if not AbstractFoundationStack.acceptsCards(_, from_stack, cards):
  18852.             return 0
  18853.         
  18854.         if len(_.cards) > 12:
  18855.             return cards[0].rank == 25 - len(_.cards)
  18856.         else:
  18857.             return cards[0].rank == len(_.cards)
  18858.  
  18859.  
  18860.  
  18861. class UnionSquare_RowStack(OpenStack):
  18862.     
  18863.     def __init__(_, x, y, game, **cap):
  18864.         kwdefault(cap, mod = 8192, dir = 0, base_rank = ANY_RANK, max_accept = 1, max_move = 1)
  18865.         apply(OpenStack.__init__, (_, x, y, game), cap)
  18866.         _.CARD_YOFFSET = 1
  18867.  
  18868.     
  18869.     def acceptsCards(_, from_stack, cards):
  18870.         if not OpenStack.acceptsCards(_, from_stack, cards):
  18871.             return 0
  18872.         
  18873.         if not (_.cards):
  18874.             return 1
  18875.         
  18876.         if cards[0].suit != _.cards[0].suit:
  18877.             return 0
  18878.         
  18879.         if len(_.cards) == 1:
  18880.             card_dir = cards[0].rank - _.cards[-1].rank
  18881.             if not card_dir == 1:
  18882.                 pass
  18883.             return card_dir == -1
  18884.         else:
  18885.             stack_dir = (_.cards[1].rank - _.cards[0].rank) % _.cap.mod
  18886.             return (_.cards[-1].rank + stack_dir) % _.cap.mod == cards[0].rank
  18887.  
  18888.     
  18889.     def getBottomImage(_):
  18890.         return _.game.app.images.getReserveBottom()
  18891.  
  18892.  
  18893.  
  18894. class UnionSquare(Game):
  18895.     Hint_Class = CautiousDefaultHint
  18896.     
  18897.     def createGame(_):
  18898.         (l, s) = (Layout(_, YM = 18), _.s)
  18899.         _.setSize(l.XM + 9 * l.XS, l.YM + 4 * l.YS)
  18900.         (x, y) = (l.XM, l.YM)
  18901.         s.talon = WasteTalonStack(x, y, _, max_rounds = 1)
  18902.         l.createText(s.talon, 's')
  18903.         x = x + l.XS
  18904.         s.waste = WasteStack(x, y, _)
  18905.         l.createText(s.waste, 's')
  18906.         for i in range(4):
  18907.             x = 3 * l.XS
  18908.             for j in range(4):
  18909.                 s.rows.append(UnionSquare_RowStack(x, y, _))
  18910.                 x = x + l.XS
  18911.             
  18912.             y = y + l.YS
  18913.         
  18914.         (x, y) = (8 * l.XS, l.YM)
  18915.         for i in range(4):
  18916.             stack = UnionSquare_Foundation(x, y, _, i, max_move = 0, dir = 0, max_cards = 26)
  18917.             l.createText(stack, 'sw')
  18918.             s.foundations.append(stack)
  18919.             y = y + l.YS
  18920.         
  18921.         l.defaultStackGroups()
  18922.  
  18923.     
  18924.     def startGame(_):
  18925.         _.startDealSample()
  18926.         _.s.talon.dealRow()
  18927.         _.s.talon.dealCards()
  18928.  
  18929.     
  18930.     def shallHighlightMatch(_, stack1, card1, stack2, card2):
  18931.         if not card1.suit == card2.suit and card1.rank + 1 == card2.rank:
  18932.             pass
  18933.         return card2.rank + 1 == card1.rank
  18934.  
  18935.     
  18936.     def getHighlightPilesStacks(_):
  18937.         return ()
  18938.  
  18939.  
  18940. registerGame(GameInfo(35, UnionSquare, 'Union Square', GI.GT_2DECK_TYPE, 2, 0))
  18941.  
  18942. class Windmill_Foundation(RK_FoundationStack):
  18943.     
  18944.     def getBottomImage(_):
  18945.         if _.cap.base_rank == ACE:
  18946.             return _.game.app.images.getLetter(ACE)
  18947.         
  18948.         return RK_FoundationStack.getBottomImage(_)
  18949.  
  18950.  
  18951.  
  18952. class Windmill_RowStack(ReserveStack):
  18953.     
  18954.     def acceptsCards(_, from_stack, cards):
  18955.         if not ReserveStack.acceptsCards(_, from_stack, cards):
  18956.             return 0
  18957.         
  18958.         return from_stack is _.game.s.waste
  18959.  
  18960.  
  18961.  
  18962. class Windmill(Game):
  18963.     
  18964.     def createGame(_):
  18965.         (l, s) = (Layout(_, XM = 20), _.s)
  18966.         _.setSize(7 * l.XS + l.XM, 5 * l.YS + l.YM + l.YM)
  18967.         x = l.XM
  18968.         y = l.YM
  18969.         s.talon = WasteTalonStack(x, y, _, max_rounds = 1)
  18970.         l.createText(s.talon, 'ss')
  18971.         x = x + l.XS
  18972.         s.waste = WasteStack(x, y, _)
  18973.         l.createText(s.waste, 'ss')
  18974.         (x0, y0) = (x + l.XS, y)
  18975.         for d in ((2, 0), (2, 1), (0, 2), (1, 2), (3, 2), (4, 2), (2, 3), (2, 4)):
  18976.             (x, y) = (x0 + d[0] * l.XS, y0 + d[1] * l.YS)
  18977.             s.rows.append(Windmill_RowStack(x, y, _))
  18978.         
  18979.         (x, y) = (x0 + 2 * l.XS, y0 + 2 * l.YS)
  18980.         s.foundations.append(Windmill_Foundation(x, y, _, mod = 13, min_cards = 1, max_cards = 52))
  18981.         for d in ((1, 0.6), (3, 0.6), (1, 3.4), (3, 3.4)):
  18982.             (x, y) = (x0 + d[0] * l.XS, y0 + d[1] * l.YS)
  18983.             s.foundations.append(Windmill_Foundation(x, y, _, base_rank = KING, dir = -1))
  18984.         
  18985.         l.defaultStackGroups()
  18986.  
  18987.     
  18988.     def _shuffleHook(_, cards):
  18989.         for c in cards:
  18990.             pass
  18991.         
  18992.         cards.remove(c)
  18993.         return cards + [
  18994.             c]
  18995.  
  18996.     
  18997.     def startGame(_):
  18998.         _.startDealSample()
  18999.         _.s.talon.dealRow(rows = (_.s.foundations[0],))
  19000.         _.s.talon.dealRow()
  19001.         _.s.talon.dealCards()
  19002.  
  19003.     
  19004.     def fillStack(_, stack):
  19005.         if len(stack.cards) == 0:
  19006.             if stack is _.s.waste and _.s.talon.cards:
  19007.                 _.s.talon.dealCards()
  19008.             elif stack in _.s.rows and _.s.waste.cards:
  19009.                 _.s.waste.moveMove(1, stack)
  19010.             
  19011.         
  19012.  
  19013.     
  19014.     def shallHighlightMatch(_, stack1, card1, stack2, card2):
  19015.         if not card1.rank + 1 == card2.rank:
  19016.             pass
  19017.         return card2.rank + 1 == card1.rank
  19018.  
  19019.     
  19020.     def getHighlightPilesStacks(_):
  19021.         return ()
  19022.  
  19023.     
  19024.     def getAutoStacks(_, event = None):
  19025.         return ((), (), ())
  19026.  
  19027.  
  19028. registerGame(GameInfo(30, Windmill, 'Windmill', GI.GT_2DECK_TYPE, 2, 0))
  19029.  
  19030. class Pyramid_Hint(DefaultHint):
  19031.     
  19032.     def step010(_, dropstacks, rows):
  19033.         rows = rows + (_.game.s.talon,)
  19034.         return DefaultHint.step010(_, dropstacks, rows)
  19035.  
  19036.  
  19037.  
  19038. class Pyramid_StackMethods:
  19039.     
  19040.     def acceptsCards(_, from_stack, cards):
  19041.         if _.basicIsBlocked():
  19042.             return 0
  19043.         
  19044.         if from_stack is _ and not (_.cards) or len(cards) != 1:
  19045.             return 0
  19046.         
  19047.         c = _.cards[-1]
  19048.         if c.face_up and cards[0].face_up:
  19049.             pass
  19050.         return cards[0].rank + c.rank == 11
  19051.  
  19052.     
  19053.     def _dropKingClickHandler(_, event):
  19054.         if not (_.cards):
  19055.             return 0
  19056.         
  19057.         c = _.cards[-1]
  19058.         if c.face_up and c.rank == KING and not _.basicIsBlocked():
  19059.             _.game.playSample('autodrop', priority = 20)
  19060.             _.playMoveMove(1, _.game.s.foundations[0], sound = 0)
  19061.             return 1
  19062.         
  19063.         return 0
  19064.  
  19065.     
  19066.     def _dropPairMove(_, n, other_stack, frames = -1, shadow = -1):
  19067.         _.game.playSample('droppair', priority = 200)
  19068.         if __debug__:
  19069.             if not n == 1 and _.acceptsCards(other_stack, [
  19070.                 other_stack.cards[-1]]):
  19071.                 raise AssertionError
  19072.         old_state = _.game.enterState(_.game.S_FILL)
  19073.         f = _.game.s.foundations[0]
  19074.         _.game.moveMove(n, _, f, frames = frames, shadow = shadow)
  19075.         _.game.moveMove(n, other_stack, f, frames = frames, shadow = shadow)
  19076.         _.game.leaveState(old_state)
  19077.         _.fillStack()
  19078.         other_stack.fillStack()
  19079.  
  19080.     
  19081.     def moveMove(_, ncards, to_stack, frames = -1, shadow = -1):
  19082.         if to_stack in _.game.s.foundations:
  19083.             _.game.moveMove(ncards, _, to_stack, frames = frames, shadow = shadow)
  19084.             _.fillStack()
  19085.         else:
  19086.             _._dropPairMove(ncards, to_stack, frames = -1, shadow = shadow)
  19087.  
  19088.  
  19089.  
  19090. class Pyramid_Foundation(AbstractFoundationStack):
  19091.     
  19092.     def acceptsCards(_, from_stack, cards):
  19093.         if not AbstractFoundationStack.acceptsCards(_, from_stack, cards):
  19094.             return 0
  19095.         
  19096.         return cards[0].rank == KING
  19097.  
  19098.  
  19099.  
  19100. class Pyramid_Talon(Pyramid_StackMethods, FaceUpWasteTalonStack):
  19101.     
  19102.     def clickHandler(_, event):
  19103.         if _._dropKingClickHandler(event):
  19104.             return 1
  19105.         
  19106.         return FaceUpWasteTalonStack.clickHandler(_, event)
  19107.  
  19108.     
  19109.     def canDealCards(_):
  19110.         if not FaceUpWasteTalonStack.canDealCards(_):
  19111.             return 0
  19112.         
  19113.         return not _.game.isGameWon()
  19114.  
  19115.     
  19116.     def canDropCards(_, stacks):
  19117.         if _.cards:
  19118.             cards = _.cards[-1:]
  19119.             for s in stacks:
  19120.                 pass
  19121.             
  19122.         
  19123.         return (None, 0)
  19124.  
  19125.  
  19126.  
  19127. class Pyramid_Waste(Pyramid_StackMethods, WasteStack):
  19128.     
  19129.     def clickHandler(_, event):
  19130.         if _._dropKingClickHandler(event):
  19131.             return 1
  19132.         
  19133.         return WasteStack.clickHandler(_, event)
  19134.  
  19135.  
  19136.  
  19137. class Pyramid_RowStack(Pyramid_StackMethods, OpenStack):
  19138.     
  19139.     def __init__(_, x, y, game):
  19140.         OpenStack.__init__(_, x, y, game, max_accept = 1, max_cards = 2)
  19141.         _.CARD_YOFFSET = 1
  19142.  
  19143.     STEP = (1, 2, 2, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6)
  19144.     
  19145.     def basicIsBlocked(_):
  19146.         (r, step) = (_.game.s.rows, _.STEP)
  19147.         (i, n) = (_.id, 1)
  19148.         while i < 21:
  19149.             i = i + step[i]
  19150.             n = n + 1
  19151.             for j in range(i, i + n):
  19152.                 pass
  19153.             
  19154.             continue
  19155.             None if r[j].cards else range(i, i + n)
  19156.         return 0
  19157.  
  19158.     
  19159.     def clickHandler(_, event):
  19160.         if _._dropKingClickHandler(event):
  19161.             return 1
  19162.         
  19163.         return OpenStack.clickHandler(_, event)
  19164.  
  19165.  
  19166.  
  19167. class Pyramid(Game):
  19168.     Hint_Class = Pyramid_Hint
  19169.     
  19170.     def createGame(_, rows = 4):
  19171.         (l, s) = (Layout(_), _.s)
  19172.         _.setSize(l.XM + 9 * l.XS, l.YM + 4 * l.YS)
  19173.         for i in range(7):
  19174.             x = l.XM + (8 - i) * l.XS / 2
  19175.             y = l.YM + i * l.YS / 2
  19176.             for j in range(i + 1):
  19177.                 s.rows.append(Pyramid_RowStack(x, y, _))
  19178.                 x = x + l.XS
  19179.             
  19180.         
  19181.         (x, y) = (l.XM, l.YM)
  19182.         s.talon = Pyramid_Talon(x, y, _, max_rounds = 3, max_accept = 1)
  19183.         l.createText(s.talon, 'se')
  19184.         (tx, ty, ta, tf) = l.getTextAttr(s.talon, 'ne')
  19185.         s.talon.texts.rounds = MfxCanvasText(_.canvas, tx, ty, anchor = ta)
  19186.         y = y + l.YS
  19187.         s.waste = Pyramid_Waste(x, y, _, max_accept = 1)
  19188.         l.createText(s.waste, 'se')
  19189.         (x, y) = (_.width - l.XS, l.YM)
  19190.         s.foundations.append(Pyramid_Foundation(x, y, _, suit = ANY_SUIT, dir = 0, base_rank = ANY_RANK, max_move = 0, max_cards = 52))
  19191.         _.sg.talonstacks = [
  19192.             s.talon] + [
  19193.             s.waste]
  19194.         _.sg.openstacks = s.rows + _.sg.talonstacks
  19195.         _.sg.dropstacks = s.rows + _.sg.talonstacks
  19196.  
  19197.     
  19198.     def startGame(_):
  19199.         _.startDealSample()
  19200.         _.s.talon.dealRow()
  19201.         _.s.talon.dealCards()
  19202.  
  19203.     
  19204.     def getAutoStacks(_, event = None):
  19205.         return (_.sg.dropstacks, _.sg.dropstacks, ())
  19206.  
  19207.     
  19208.     def shallHighlightMatch(_, stack1, card1, stack2, card2):
  19209.         return card1.rank + card2.rank == 11
  19210.  
  19211.  
  19212.  
  19213. class RelaxedPyramid(Pyramid):
  19214.     
  19215.     def isGameWon(_):
  19216.         return getNumberOfFreeStacks(_.s.rows) == len(_.s.rows)
  19217.  
  19218.  
  19219.  
  19220. class Thirteen(Pyramid):
  19221.     
  19222.     def createGame(_):
  19223.         (l, s) = (Layout(_), _.s)
  19224.         _.setSize(7 * l.XS + l.XM, 5 * l.YS + l.YM)
  19225.         for i in range(7):
  19226.             x = l.XM + (6 - i) * l.XS / 2
  19227.             y = l.YM + l.YS + i * l.YS / 2
  19228.             for j in range(i + 1):
  19229.                 s.rows.append(Pyramid_RowStack(x, y, _))
  19230.                 x = x + l.XS
  19231.             
  19232.         
  19233.         (x, y) = (l.XM, l.YM)
  19234.         s.talon = WasteTalonStack(x, y, _, max_rounds = 1)
  19235.         l.createText(s.talon, 's')
  19236.         x = x + l.XS
  19237.         s.waste = Pyramid_Waste(x, y, _)
  19238.         l.createText(s.waste, 's')
  19239.         s.waste.CARD_XOFFSET = 14
  19240.         (x, y) = (_.width - l.XS, l.YM)
  19241.         s.foundations.append(Pyramid_Foundation(x, y, _, suit = ANY_SUIT, dir = 0, base_rank = ANY_RANK, max_move = 0, max_cards = 999999))
  19242.         _.sg.talonstacks = [
  19243.             s.talon] + [
  19244.             s.waste]
  19245.         _.sg.openstacks = s.rows + _.sg.talonstacks
  19246.         _.sg.dropstacks = s.rows + _.sg.talonstacks
  19247.  
  19248.     
  19249.     def startGame(_):
  19250.         _.startDealSample()
  19251.         _.s.talon.dealRow(rows = _.s.rows[:21], flip = 0)
  19252.         _.s.talon.dealRow(rows = _.s.rows[21:])
  19253.         _.s.talon.dealCards()
  19254.  
  19255.  
  19256. registerGame(GameInfo(38, Pyramid, 'Pyramid', GI.GT_PAIRING_TYPE, 1, 2))
  19257. registerGame(GameInfo(193, RelaxedPyramid, 'Relaxed Pyramid', GI.GT_PAIRING_TYPE | GI.GT_RELAXED, 1, 2))
  19258.  
  19259. class PileOn_RowStack(RK_RowStack):
  19260.     
  19261.     def getBottomImage(_):
  19262.         return _.game.app.images.getReserveBottom()
  19263.  
  19264.  
  19265.  
  19266. class PileOn(Game):
  19267.     Hint_Class = DefaultHint
  19268.     
  19269.     def createGame(_, playcards = 4):
  19270.         (l, s) = (Layout(_, XOFFSET = 16), _.s)
  19271.         w = max(2 * l.XS, l.XS + (playcards - 1) * l.XOFFSET + 2 * l.XM)
  19272.         _.setSize(l.XM + 4 * w, l.YM + 4 * l.YS)
  19273.         y = l.YM
  19274.         for i in range(4):
  19275.             x = l.XM
  19276.             for j in range(4):
  19277.                 stack = PileOn_RowStack(x, y, _, dir = 0, max_cards = 4)
  19278.                 (stack.CARD_XOFFSET, stack.CARD_YOFFSET) = (l.XOFFSET, 0)
  19279.                 s.rows.append(stack)
  19280.                 x = x + w
  19281.             
  19282.             y = y + l.YS
  19283.         
  19284.         (x, y) = (_.width - l.XS, _.height - l.YS)
  19285.         s.talon = InitialDealTalonStack(x, y, _)
  19286.         _.sg.openstacks = s.rows
  19287.         _.sg.talonstacks = [
  19288.             s.talon]
  19289.         _.sg.dropstacks = s.rows
  19290.  
  19291.     
  19292.     def startGame(_):
  19293.         r = _.s.rows[:13]
  19294.         for i in range(3):
  19295.             _.s.talon.dealRow(rows = r, frames = 0)
  19296.         
  19297.         _.startDealSample()
  19298.         _.s.talon.dealRow(rows = r)
  19299.         if not __debug__ and len(_.s.talon.cards) == 0:
  19300.             raise AssertionError
  19301.         0
  19302.  
  19303.     
  19304.     def isGameWon(_):
  19305.         for r in _.s.rows:
  19306.             if r.cards:
  19307.                 if len(r.cards) != 4 or not r._isSequence(r.cards):
  19308.                     return 0
  19309.                 
  19310.             
  19311.         
  19312.         return 1
  19313.  
  19314.     
  19315.     def shallHighlightMatch(_, stack1, card1, stack2, card2):
  19316.         return card1.rank == card2.rank
  19317.  
  19318.  
  19319. registerGame(GameInfo(41, PileOn, 'PileOn', GI.GT_1DECK_TYPE, 1, 0))
  19320.  
  19321. class Bristol_Hint(CautiousDefaultHint):
  19322.     BONUS_CREATE_EMPTY_ROW = 0
  19323.     BONUS_CAN_DROP_ALL_CARDS = 0
  19324.     BONUS_CAN_CREATE_EMPTY_ROW = 0
  19325.     
  19326.     def _getMovePileScore(_, score, color, r, t, pile, rpile):
  19327.         if not (r in _.game.s.reserves):
  19328.             score = score - 10000
  19329.             if len(pile) == len(r.cards):
  19330.                 return (-1, color)
  19331.             
  19332.         
  19333.         return CautiousDefaultHint._getMovePileScore(_, score, color, r, t, pile, rpile)
  19334.  
  19335.  
  19336.  
  19337. class Bristol_Talon(TalonStack):
  19338.     
  19339.     def dealCards(_, sound = 0):
  19340.         return _.dealRowAvail(rows = _.game.s.reserves, sound = sound)
  19341.  
  19342.  
  19343.  
  19344. class Bristol(Game):
  19345.     Layout_Method = Layout.klondikeLayout
  19346.     Hint_Class = Bristol_Hint
  19347.     
  19348.     def createGame(_, **layout):
  19349.         (l, s) = (Layout(_, XOFFSET = 10), _.s)
  19350.         _.setSize(l.XM + 10 * l.XS, l.YM + 5 * l.YS)
  19351.         (x, y) = (l.XM + 3 * l.XS, l.YM)
  19352.         for i in range(4):
  19353.             s.foundations.append(RK_FoundationStack(x, y, _, max_move = 0))
  19354.             x = x + l.XS
  19355.         
  19356.         for i in range(2):
  19357.             y = l.YM + (i * 2 + 3) * l.YS / 2
  19358.             for j in range(4):
  19359.                 x = l.XM + j * 5 * l.XS / 2
  19360.                 stack = RK_RowStack(x, y, _, base_rank = NO_RANK, max_move = 1)
  19361.                 (stack.CARD_XOFFSET, stack.CARD_YOFFSET) = (l.XOFFSET, 0)
  19362.                 s.rows.append(stack)
  19363.             
  19364.         
  19365.         (x, y) = (l.XM + 3 * l.XS, l.YM + 4 * l.YS)
  19366.         s.talon = Bristol_Talon(x, y, _)
  19367.         l.createText(s.talon, 'sw')
  19368.         for i in range(3):
  19369.             x = x + l.XS
  19370.             s.reserves.append(ReserveStack(x, y, _, max_accept = 0, max_cards = 999999))
  19371.         
  19372.         _.sg.openstacks = s.foundations + s.rows
  19373.         _.sg.talonstacks = [
  19374.             s.talon]
  19375.         _.sg.dropstacks = s.rows + s.reserves
  19376.  
  19377.     
  19378.     def _shuffleHook(_, cards):
  19379.         (i, n) = (0, len(_.s.rows))
  19380.         kings = []
  19381.         for c in cards[:24]:
  19382.             i = i + 1
  19383.         
  19384.         for i in kings:
  19385.             j = i % n
  19386.             while j < i:
  19387.                 j = j + n
  19388.                 continue
  19389.                 None if c.rank == KING else cards[:24] if cards[j].rank != KING else kings
  19390.         
  19391.         cards.reverse()
  19392.         return cards
  19393.  
  19394.     
  19395.     def startGame(_):
  19396.         r = _.s.rows
  19397.         for i in range(2):
  19398.             _.s.talon.dealRow(rows = r, frames = 0)
  19399.         
  19400.         _.startDealSample()
  19401.         _.s.talon.dealRow(rows = r)
  19402.         _.s.talon.dealCards()
  19403.  
  19404.  
  19405.  
  19406. class Belvedere(Bristol):
  19407.     
  19408.     def _shuffleHook(_, cards):
  19409.         for c in cards:
  19410.             pass
  19411.         
  19412.         cards = Bristol._shuffleHook(_, cards)
  19413.         return cards[:-24] + [
  19414.             c] + cards[-24:]
  19415.  
  19416.     
  19417.     def startGame(_):
  19418.         r = _.s.rows
  19419.         for i in range(2):
  19420.             _.s.talon.dealRow(rows = r, frames = 0)
  19421.         
  19422.         _.startDealSample()
  19423.         _.s.talon.dealRow(rows = r)
  19424.         if not __debug__ and _.s.talon.cards[-1].rank == ACE:
  19425.             raise AssertionError
  19426.         0
  19427.         _.s.talon.dealRow(rows = _.s.foundations[:1])
  19428.         _.s.talon.dealCards()
  19429.  
  19430.  
  19431. registerGame(GameInfo(42, Bristol, 'Bristol', GI.GT_FAN_TYPE, 1, 0))
  19432. registerGame(GameInfo(214, Belvedere, 'Belvedere', GI.GT_FAN_TYPE, 1, 0))
  19433.  
  19434. class AcesUp_Foundation(AbstractFoundationStack):
  19435.     
  19436.     def acceptsCards(_, from_stack, cards):
  19437.         if not AbstractFoundationStack.acceptsCards(_, from_stack, cards):
  19438.             return 0
  19439.         
  19440.         c = cards[0]
  19441.         for s in _.game.s.rows:
  19442.             if s is not from_stack and s.cards and s.cards[-1].suit == c.suit:
  19443.                 if s.cards[-1].rank > c.rank or s.cards[-1].rank == ACE:
  19444.                     return c.rank != ACE
  19445.                 
  19446.             
  19447.         
  19448.         return 0
  19449.  
  19450.  
  19451.  
  19452. class AcesUp_RowStack(BasicRowStack):
  19453.     
  19454.     def acceptsCards(_, from_stack, cards):
  19455.         if not BasicRowStack.acceptsCards(_, from_stack, cards):
  19456.             return 0
  19457.         
  19458.         return len(_.cards) == 0
  19459.  
  19460.     clickHandler = BasicRowStack.doubleclickHandler
  19461.  
  19462.  
  19463. class AcesUp(Game):
  19464.     Talon_Class = DealRowTalonStack
  19465.     RowStack_Class = StackWrapper(AcesUp_RowStack, max_accept = 1)
  19466.     
  19467.     def createGame(_, **layout):
  19468.         (l, s) = (Layout(_), _.s)
  19469.         _.setSize(l.XM + 7 * l.XS, l.YM + 4 * l.YS)
  19470.         (x, y) = (l.XM, l.YM)
  19471.         s.talon = _.Talon_Class(x, y, _)
  19472.         l.createText(s.talon, 'ss')
  19473.         x = x + 3 * l.XS / 2
  19474.         for i in range(4):
  19475.             s.rows.append(_.RowStack_Class(x, y, _))
  19476.             x = x + l.XS
  19477.         
  19478.         x = l.XM + 6 * l.XS
  19479.         stack = AcesUp_Foundation(x, y, _, ANY_SUIT, max_move = 0, dir = 0, base_rank = ANY_RANK, max_cards = 48)
  19480.         l.createText(stack, 'ss')
  19481.         s.foundations.append(stack)
  19482.         l.defaultStackGroups()
  19483.  
  19484.     
  19485.     def startGame(_):
  19486.         _.startDealSample()
  19487.         _.s.talon.dealRow()
  19488.  
  19489.     
  19490.     def isGameWon(_):
  19491.         if len(_.s.foundations[0].cards) != 48:
  19492.             return 0
  19493.         
  19494.         for s in _.s.rows:
  19495.             pass
  19496.         
  19497.         return 1
  19498.  
  19499.     
  19500.     def getAutoStacks(_, event = None):
  19501.         if event is None:
  19502.             return (_.sg.dropstacks, (), _.sg.dropstacks)
  19503.         else:
  19504.             return (_.sg.dropstacks, _.sg.dropstacks, _.sg.dropstacks)
  19505.  
  19506.  
  19507.  
  19508. class Fortunes(AcesUp):
  19509.     RowStack_Class = StackWrapper(AcesUp_RowStack, max_move = 999999, max_accept = 999999)
  19510.  
  19511.  
  19512. class RussianAces_Talon(DealRowTalonStack):
  19513.     
  19514.     def dealCards(_, sound = 0):
  19515.         rows = filter((lambda s: not (s.cards)), _.game.s.rows)
  19516.         if not rows:
  19517.             rows = _.game.s.rows
  19518.         
  19519.         return _.dealRowAvail(rows = rows, sound = sound)
  19520.  
  19521.  
  19522.  
  19523. class RussianAces(AcesUp):
  19524.     Talon_Class = RussianAces_Talon
  19525.  
  19526.  
  19527. class PerpetualMotion_Talon(DealRowTalonStack):
  19528.     
  19529.     def canDealCards(_):
  19530.         if _.game.demo and _.game.moves.index >= 500:
  19531.             return 0
  19532.         
  19533.         return not _.game.isGameWon()
  19534.  
  19535.     
  19536.     def dealCards(_, sound = 0):
  19537.         if _.cards:
  19538.             return DealRowTalonStack.dealCards(_, sound = sound)
  19539.         
  19540.         (game, num_cards) = (_.game, len(_.cards))
  19541.         rows = list(game.s.rows)[:]
  19542.         rows.reverse()
  19543.         for r in rows:
  19544.             while r.cards:
  19545.                 num_cards = num_cards + 1
  19546.                 game.moveMove(1, r, _, frames = 4)
  19547.                 continue
  19548.                 None if _.cards[-1].face_up else rows
  19549.         
  19550.         if not __debug__ and len(_.cards) == num_cards:
  19551.             raise AssertionError
  19552.         return DealRowTalonStack.dealCards(_, sound = sound)
  19553.  
  19554.  
  19555.  
  19556. class PerpetualMotion_Foundation(AbstractFoundationStack):
  19557.     
  19558.     def acceptsCards(_, from_stack, cards):
  19559.         if not AbstractFoundationStack.acceptsCards(_, from_stack, cards):
  19560.             return 0
  19561.         
  19562.         return isRankSequence(cards, dir = 0)
  19563.  
  19564.  
  19565.  
  19566. class PerpetualMotion_RowStack(RK_RowStack):
  19567.     
  19568.     def canDropCards(_, stacks):
  19569.         pile = _.getPile()
  19570.         if not pile or len(pile) != 4:
  19571.             return (None, 0)
  19572.         
  19573.         for s in stacks:
  19574.             pass
  19575.         
  19576.         return (None, 0)
  19577.  
  19578.  
  19579.  
  19580. class PerpetualMotion(Game):
  19581.     
  19582.     def createGame(_, **layout):
  19583.         (l, s) = (Layout(_), _.s)
  19584.         _.setSize(l.XM + 7 * l.XS, l.YM + 4 * l.YS)
  19585.         (x, y) = (l.XM, l.YM)
  19586.         s.talon = PerpetualMotion_Talon(x, y, _, max_rounds = -1)
  19587.         l.createText(s.talon, 'ss')
  19588.         x = x + 3 * l.XS / 2
  19589.         for i in range(4):
  19590.             s.rows.append(PerpetualMotion_RowStack(x, y, _, dir = 0, base_rank = NO_RANK))
  19591.             x = x + l.XS
  19592.         
  19593.         x = l.XM + 6 * l.XS
  19594.         stack = PerpetualMotion_Foundation(x, y, _, ANY_SUIT, base_rank = ANY_RANK, max_cards = 52, max_move = 0, min_accept = 4, max_accept = 4)
  19595.         l.createText(stack, 'ss')
  19596.         s.foundations.append(stack)
  19597.         l.defaultStackGroups()
  19598.  
  19599.     
  19600.     def startGame(_):
  19601.         _.startDealSample()
  19602.         _.s.talon.dealRow()
  19603.  
  19604.     
  19605.     def shallHighlightMatch(_, stack1, card1, stack2, card2):
  19606.         return card1.rank == card2.rank
  19607.  
  19608.  
  19609. registerGame(GameInfo(903, AcesUp, 'Aces Up', GI.GT_1DECK_TYPE, 1, 0, altnames = "Idiot's Delight"))
  19610. registerGame(GameInfo(206, Fortunes, 'Fortunes', GI.GT_1DECK_TYPE, 1, 0))
  19611. registerGame(GameInfo(213, RussianAces, 'Russian Aces', GI.GT_1DECK_TYPE, 1, 0))
  19612. registerGame(GameInfo(130, PerpetualMotion, 'Perpetual Motion', GI.GT_1DECK_TYPE, 1, -1, altnames = 'First Law'))
  19613.  
  19614. class Montana_Hint(DefaultHint):
  19615.     
  19616.     def computeHints(_):
  19617.         game = _.game
  19618.         (RLEN, RSTEP, RBASE) = (game.RLEN, game.RSTEP, game.RBASE)
  19619.         freerows = filter((lambda s: not (s.cards)), game.s.rows)
  19620.         for r in game.s.rows:
  19621.             if __debug__:
  19622.                 if not len(r.cards) == 1 and r.cards[-1].face_up:
  19623.                     raise AssertionError
  19624.             None if not (r.cards) else game.s.rows
  19625.             (c, pile, rpile) = (r.cards[0], r.cards, [])
  19626.             if r.id % RSTEP > 0:
  19627.                 left = game.s.rows[r.id - 1]
  19628.             else:
  19629.                 left = None
  19630.                 if c.rank == RBASE:
  19631.                     continue
  19632.                 
  19633.             for t in freerows:
  19634.                 if _.shallMovePile(r, t, pile, rpile):
  19635.                     _.addHint(score, 1, r, t)
  19636.                 
  19637.             
  19638.         
  19639.  
  19640.  
  19641.  
  19642. class Montana_Talon(TalonStack):
  19643.     
  19644.     def canDealCards(_):
  19645.         if _.round != _.max_rounds:
  19646.             pass
  19647.         return not _.game.isGameWon()
  19648.  
  19649.     
  19650.     def dealCards(_, sound = 0):
  19651.         game = _.game
  19652.         (RLEN, RSTEP, RBASE) = (game.RLEN, game.RSTEP, game.RBASE)
  19653.         num_cards = 0
  19654.         if not __debug__ and len(_.cards) == 0:
  19655.             raise AssertionError
  19656.         rows = game.s.rows
  19657.         stacks = []
  19658.         gaps = [
  19659.             None] * 4
  19660.         for g in range(4):
  19661.             i = g * RSTEP
  19662.             r = rows[i]
  19663.             for j in range(RSTEP):
  19664.                 r = rows[i + j]
  19665.                 if not in_sequence:
  19666.                     stacks.append(r)
  19667.                     if gaps[g] is None:
  19668.                         gaps[g] = r
  19669.                     
  19670.                     if r.cards:
  19671.                         game.moveMove(1, r, _, frames = 0)
  19672.                         num_cards = num_cards + 1
  19673.                     
  19674.                 
  19675.             
  19676.         
  19677.         if not __debug__ and len(_.cards) == num_cards:
  19678.             raise AssertionError
  19679.         if not __debug__ and len(stacks) == num_cards + len(gaps):
  19680.             raise AssertionError
  19681.         if num_cards == 0:
  19682.             return 0
  19683.         
  19684.         if sound:
  19685.             game.startDealSample()
  19686.         
  19687.         game.shuffleStackMove(_)
  19688.         game.nextRoundMove(_)
  19689.         spaces = _.getRedealSpaces(stacks, gaps)
  19690.         for r in stacks:
  19691.             pass
  19692.         
  19693.         if not __debug__ and len(_.cards) == 0:
  19694.             raise AssertionError
  19695.         None if not (r in spaces) else stacks
  19696.         if sound:
  19697.             game.stopSamples()
  19698.         
  19699.         return num_cards
  19700.  
  19701.     
  19702.     def getRedealSpaces(_, stacks, gaps):
  19703.         return gaps
  19704.  
  19705.  
  19706.  
  19707. class Montana_RowStack(BasicRowStack):
  19708.     
  19709.     def acceptsCards(_, from_stack, cards):
  19710.         if not BasicRowStack.acceptsCards(_, from_stack, cards):
  19711.             return 0
  19712.         
  19713.         if _.id % _.game.RSTEP == 0:
  19714.             return cards[0].rank == _.game.RBASE
  19715.         
  19716.         left = _.game.s.rows[_.id - 1]
  19717.         if left.cards and left.cards[-1].suit == cards[0].suit:
  19718.             pass
  19719.         return left.cards[-1].rank + 1 == cards[0].rank
  19720.  
  19721.     
  19722.     def clickHandler(_, event):
  19723.         if not (_.cards):
  19724.             return _.quickPlayHandler(event)
  19725.         
  19726.         return BasicRowStack.clickHandler(_, event)
  19727.  
  19728.     prepareBottom = Stack.prepareInvisibleBottom
  19729.     
  19730.     def getBottomImage(_):
  19731.         return _.game.app.images.getReserveBottom()
  19732.  
  19733.  
  19734.  
  19735. class Montana(Game):
  19736.     Talon_Class = Montana_Talon
  19737.     RowStack_Class = Montana_RowStack
  19738.     Hint_Class = Montana_Hint
  19739.     (RLEN, RSTEP, RBASE) = (52, 13, 1)
  19740.     
  19741.     def createGame(_):
  19742.         (l, s) = (Layout(_, XM = 4), _.s)
  19743.         _.setSize(l.XM + _.RSTEP * l.XS, l.YM + 5 * l.YS)
  19744.         for i in range(4):
  19745.             (x, y) = (l.XM, l.YM + i * l.YS)
  19746.             for j in range(_.RSTEP):
  19747.                 s.rows.append(_.RowStack_Class(x, y, _, max_accept = 1, max_cards = 1))
  19748.                 x = x + l.XS
  19749.             
  19750.         
  19751.         x = l.XM + (_.RSTEP - 1) * l.XS / 2
  19752.         s.talon = _.Talon_Class(x, _.height - l.YS, _, max_rounds = 3)
  19753.         l.defaultStackGroups()
  19754.  
  19755.     
  19756.     def startGame(_):
  19757.         frames = 0
  19758.         for i in range(52):
  19759.             c = _.s.talon.cards[-1]
  19760.             if c.rank == ACE:
  19761.                 _.s.talon.dealRow(rows = _.s.internals, frames = 0)
  19762.             elif frames == 0 and i >= 39:
  19763.                 _.startDealSample()
  19764.                 frames = 4
  19765.             
  19766.             _.s.talon.dealRow(rows = (_.s.rows[i],), frames = frames)
  19767.         
  19768.         if not __debug__ and len(_.s.talon.cards) == 0:
  19769.             raise AssertionError
  19770.  
  19771.     
  19772.     def isGameWon(_):
  19773.         rows = _.s.rows
  19774.         for i in range(0, _.RLEN, _.RSTEP):
  19775.             suit = rows[i].cards[-1].suit
  19776.             for j in range(_.RSTEP - 1):
  19777.                 r = rows[i + j]
  19778.             
  19779.         
  19780.         return 1
  19781.  
  19782.     
  19783.     def getHighlightPilesStacks(_):
  19784.         return ()
  19785.  
  19786.     
  19787.     def getAutoStacks(_, event = None):
  19788.         return (_.sg.dropstacks, (), _.sg.dropstacks)
  19789.  
  19790.     
  19791.     def shallHighlightMatch(_, stack1, card1, stack2, card2):
  19792.         if not card1.suit == card2.suit and card1.rank + 1 == card2.rank:
  19793.             pass
  19794.         return card2.rank + 1 == card1.rank
  19795.  
  19796.     
  19797.     def getQuickPlayScore(_, ncards, from_stack, to_stack):
  19798.         if from_stack.cards:
  19799.             if from_stack.id % _.RSTEP == 0 and from_stack.cards[-1].rank == _.RBASE:
  19800.                 return -1
  19801.             
  19802.         
  19803.         return 1
  19804.  
  19805.  
  19806.  
  19807. class Spaces_Talon(Montana_Talon):
  19808.     
  19809.     def getRedealSpaces(_, stacks, gaps):
  19810.         spaces = []
  19811.         while len(spaces) != 4:
  19812.             r = _.game.random.choice(stacks)
  19813.             if not (r in spaces):
  19814.                 spaces.append(r)
  19815.             
  19816.         return spaces
  19817.  
  19818.  
  19819.  
  19820. class Spaces(Montana):
  19821.     Talon_Class = Spaces_Talon
  19822.  
  19823.  
  19824. class BlueMoon(Montana):
  19825.     (RLEN, RSTEP, RBASE) = (56, 14, 0)
  19826.     
  19827.     def startGame(_):
  19828.         frames = 0
  19829.         j = 0
  19830.         for i in range(52):
  19831.             if i == 39:
  19832.                 _.startDealSample()
  19833.                 frames = 4
  19834.             
  19835.             _.s.talon.dealRow(rows = (_.s.rows[j],), frames = frames)
  19836.             j = j + 1
  19837.         
  19838.         if not __debug__ and len(_.s.talon.cards) == 0:
  19839.             raise AssertionError
  19840.         ace_rows = filter((lambda r: if r.cards:
  19841. passr.cards[-1].rank == ACE), _.s.rows)
  19842.         j = 0
  19843.         for r in ace_rows:
  19844.             _.moveMove(1, r, _.s.rows[j])
  19845.             j = j + 14
  19846.         
  19847.  
  19848.  
  19849.  
  19850. class RedMoon(BlueMoon):
  19851.     
  19852.     def _shuffleHook(_, cards):
  19853.         return _._shuffleHookMoveToTop(cards, (lambda c: (c.rank == 0, c.suit)))
  19854.  
  19855.     
  19856.     def startGame(_):
  19857.         frames = 0
  19858.         r = _.s.rows
  19859.         _.s.talon.dealRow(rows = (r[0], r[14], r[28], r[42]), frames = frames)
  19860.         for i in range(4):
  19861.             n = i * 14 + 2
  19862.             _.s.talon.dealRow(rows = r[n:n + 12], frames = frames)
  19863.         
  19864.  
  19865.  
  19866. registerGame(GameInfo(53, Montana, 'Montana', GI.GT_MONTANA, 1, 2, si = {
  19867.     'ncards': 48 }, altnames = 'Gaps'))
  19868. registerGame(GameInfo(116, Spaces, 'Spaces', GI.GT_MONTANA, 1, 2, si = {
  19869.     'ncards': 48 }))
  19870. registerGame(GameInfo(63, BlueMoon, 'Blue Moon', GI.GT_MONTANA, 1, 2))
  19871. registerGame(GameInfo(117, RedMoon, 'Red Moon', GI.GT_MONTANA, 1, 2))
  19872.  
  19873. class PasDeDeux_Hint(AbstractHint):
  19874.     
  19875.     def getDistance(_, stack, card):
  19876.         d = 0
  19877.         if card.rank != stack.id % 13:
  19878.             d = d + 1
  19879.         
  19880.         if card.suit != stack.id / 13:
  19881.             d = d + 1
  19882.         
  19883.         return d
  19884.  
  19885.     
  19886.     def computeHints(_):
  19887.         rows = []
  19888.         for r in _.game.s.rows:
  19889.             pass
  19890.         
  19891.         for r in rows:
  19892.             r1_d = _.getDistance(r, r.cards[-1])
  19893.             (column, row) = (r.id % 13, r.id / 13)
  19894.             stack_ids = range(column, 52, 13) + range(13 * row, 13 * row + 13)
  19895.             for i in stack_ids:
  19896.                 t = _.game.s.rows[i]
  19897.                 if not __debug__ and t.acceptsCards(r, r.cards):
  19898.                     raise AssertionError
  19899.                 0 if t is r else stack_ids
  19900.                 t1_d = _.getDistance(t, t.cards[-1])
  19901.                 r2_d = _.getDistance(t, r.cards[-1])
  19902.                 t2_d = _.getDistance(r, t.cards[-1])
  19903.                 (rw, tw) = (3, 2)
  19904.                 c = _.game.cards[t.cards[-1].id - 52]
  19905.                 if 1 and c in _.game.s.waste.cards:
  19906.                     rw = rw - 1
  19907.                 
  19908.                 score = int((rw * r1_d + tw * t1_d - (rw * r2_d + tw * t2_d)) * 1000)
  19909.                 if score > 0:
  19910.                     _.addHint(score, 1, r, t)
  19911.                 
  19912.             
  19913.         
  19914.  
  19915.  
  19916.  
  19917. class PasDeDeux_Waste(WasteStack):
  19918.     
  19919.     def canFlipCard(_):
  19920.         return 0
  19921.  
  19922.  
  19923.  
  19924. class PasDeDeux_RowStack(ReserveStack):
  19925.     
  19926.     def canMoveCards(_, cards):
  19927.         if not ReserveStack.canMoveCards(_, cards):
  19928.             return 0
  19929.         
  19930.         if not (_.game.s.waste.cards):
  19931.             return 0
  19932.         
  19933.         c = _.game.s.waste.cards[-1]
  19934.         if c.face_up and cards[0].suit == c.suit:
  19935.             pass
  19936.         return cards[0].rank == c.rank
  19937.  
  19938.     
  19939.     def acceptsCards(_, from_stack, cards):
  19940.         if not ReserveStack.acceptsCards(_, from_stack, cards):
  19941.             return 0
  19942.         
  19943.         return _.game.isNeighbour(from_stack, _)
  19944.  
  19945.     
  19946.     def moveMove(_, ncards, to_stack, frames = -1, shadow = -1):
  19947.         if __debug__:
  19948.             if not ncards == 1 and to_stack in _.game.s.rows:
  19949.                 raise AssertionError
  19950.         if not __debug__ and len(to_stack.cards) == 1:
  19951.             raise AssertionError
  19952.         _._swapPairMove(ncards, to_stack, frames = -1, shadow = 0)
  19953.         if _.game.s.talon.canDealCards():
  19954.             _.game.s.talon.dealCards()
  19955.         elif not __debug__ and _.game.s.waste.cards[-1].face_up:
  19956.             raise AssertionError
  19957.         _.game.flipMove(_.game.s.waste)
  19958.  
  19959.     
  19960.     def _swapPairMove(_, n, other_stack, frames = -1, shadow = -1):
  19961.         game = _.game
  19962.         old_state = game.enterState(game.S_FILL)
  19963.         swap = game.s.internals[0]
  19964.         game.moveMove(n, _, swap, frames = 0)
  19965.         game.moveMove(n, other_stack, _, frames = frames, shadow = shadow)
  19966.         game.moveMove(n, swap, other_stack, frames = 0)
  19967.         game.leaveState(old_state)
  19968.  
  19969.     
  19970.     def getBottomImage(_):
  19971.         suit = _.id / 13
  19972.         return _.game.app.images.getSuitBottom(suit)
  19973.  
  19974.     
  19975.     def quickPlayHandler(_, event, from_stacks = None, to_stacks = None):
  19976.         for r in _.game.s.rows:
  19977.             if r.canMoveCards(r.cards):
  19978.                 break
  19979.             
  19980.         
  19981.         return 0
  19982.  
  19983.  
  19984.  
  19985. class PasDeDeux(Game):
  19986.     Hint_Class = PasDeDeux_Hint
  19987.     
  19988.     def createGame(_):
  19989.         (l, s) = (Layout(_, XM = 4), _.s)
  19990.         _.setSize(l.XM + 13 * l.XS, l.YM + 5 * l.YS)
  19991.         for i in range(4):
  19992.             for j in range(13):
  19993.                 (x, y) = (l.XM + j * l.XS, l.YM + i * l.YS)
  19994.                 s.rows.append(PasDeDeux_RowStack(x, y, _, max_accept = 1, max_cards = 2))
  19995.             
  19996.         
  19997.         (x, y) = (_.width - 2 * l.XS, _.height - l.YS)
  19998.         s.talon = WasteTalonStack(x, y, _, max_rounds = 2)
  19999.         l.createText(s.talon, 'se')
  20000.         s.talon.texts.rounds = MfxCanvasText(_.canvas, x + l.XS, y, anchor = 'nw')
  20001.         x = x - l.XS
  20002.         s.waste = PasDeDeux_Waste(x, y, _, max_move = 0)
  20003.         l.createText(s.waste, 'sw')
  20004.         s.internals.append(InvisibleStack(_))
  20005.         l.defaultStackGroups()
  20006.  
  20007.     
  20008.     def shuffle(_):
  20009.         _.shuffleSeparateDecks()
  20010.  
  20011.     
  20012.     def startGame(_):
  20013.         _.startDealSample()
  20014.         _.s.talon.dealRow(frames = 4)
  20015.         _.s.talon.dealCards()
  20016.  
  20017.     
  20018.     def getAutoStacks(_, event = None):
  20019.         return ((), (), _.sg.dropstacks)
  20020.  
  20021.     
  20022.     def isGameWon(_):
  20023.         for r in _.s.rows:
  20024.             c = r.cards[-1]
  20025.             if c.suit != r.id / 13 or c.rank != r.id % 13:
  20026.                 return 0
  20027.             
  20028.         
  20029.         return 1
  20030.  
  20031.     
  20032.     def isNeighbour(_, stack1, stack2):
  20033.         (column1, row1) = (stack1.id % 13, stack1.id / 13)
  20034.         (column2, row2) = (stack2.id % 13, stack2.id / 13)
  20035.         if not column1 == column2:
  20036.             pass
  20037.         return row1 == row2
  20038.  
  20039.     
  20040.     def getHighlightPilesStacks(_):
  20041.         return ((_.s.rows, 1),)
  20042.  
  20043.  
  20044. registerGame(GameInfo(153, PasDeDeux, 'Pas de Deux', GI.GT_MONTANA | GI.GT_SEPARATE_DECKS, 2, 1))
  20045.  
  20046. class RoyalCotillion_Foundation(SS_FoundationStack):
  20047.     
  20048.     def getBottomImage(_):
  20049.         if _.cap.base_rank == 1:
  20050.             return _.game.app.images.getLetter(1)
  20051.         
  20052.         return _.game.app.images.getSuitBottom(_.cap.base_suit)
  20053.  
  20054.  
  20055.  
  20056. class RoyalCotillion(Game):
  20057.     Foundation_Class = RoyalCotillion_Foundation
  20058.     
  20059.     def createGame(_):
  20060.         (l, s) = (Layout(_), _.s)
  20061.         _.setSize(l.XM + 10 * l.XS, l.YM + 4 * l.YS)
  20062.         for i in range(4):
  20063.             (x, y) = (l.XM + i * l.XS, l.YM)
  20064.             s.rows.append(BasicRowStack(x, y, _, max_accept = 0))
  20065.         
  20066.         for i in range(4):
  20067.             (x, y) = (l.XM + 4 * l.XS, l.YM + i * l.YS)
  20068.             s.foundations.append(_.Foundation_Class(x, y, _, i, dir = 2, mod = 13))
  20069.             x = x + l.XS
  20070.             s.foundations.append(_.Foundation_Class(x, y, _, i, dir = 2, mod = 13, base_rank = 1))
  20071.         
  20072.         for i in range(4):
  20073.             for j in range(4):
  20074.                 (x, y) = (l.XM + (j + 6) * l.XS, l.YM + i * l.YS)
  20075.                 s.reserves.append(ReserveStack(x, y, _, max_accept = 0))
  20076.             
  20077.         
  20078.         (x, y) = (l.XM + l.XS, _.height - l.YS)
  20079.         s.talon = WasteTalonStack(x, y, _, max_rounds = 1)
  20080.         l.createText(s.talon, 'sw')
  20081.         x = x + l.XS
  20082.         s.waste = WasteStack(x, y, _)
  20083.         l.createText(s.waste, 'se')
  20084.         l.defaultStackGroups()
  20085.  
  20086.     
  20087.     def startGame(_):
  20088.         _.s.talon.dealRow(rows = _.s.reserves, frames = 0)
  20089.         _.startDealSample()
  20090.         for i in range(3):
  20091.             _.s.talon.dealRow()
  20092.         
  20093.         _.s.talon.dealCards()
  20094.  
  20095.     
  20096.     def fillStack(_, stack):
  20097.         if not (stack.cards):
  20098.             old_state = _.enterState(_.S_FILL)
  20099.             if stack is _.s.waste and _.s.talon.cards:
  20100.                 _.s.talon.dealCards()
  20101.             elif stack in _.s.reserves and _.s.waste.cards:
  20102.                 _.s.waste.moveMove(1, stack)
  20103.             
  20104.             _.leaveState(old_state)
  20105.         
  20106.  
  20107.     
  20108.     def getHighlightPilesStacks(_):
  20109.         return ()
  20110.  
  20111.     
  20112.     def getAutoStacks(_, event = None):
  20113.         if event is None:
  20114.             return (_.sg.dropstacks, (), _.sg.dropstacks)
  20115.         else:
  20116.             return (_.sg.dropstacks, _.sg.dropstacks, _.sg.dropstacks)
  20117.  
  20118.  
  20119.  
  20120. class OddAndEven(RoyalCotillion):
  20121.     
  20122.     def createGame(_):
  20123.         (l, s) = (Layout(_), _.s)
  20124.         _.setSize(l.XM + 8 * l.XS, l.YM + 4 * l.YS)
  20125.         (x, y) = (l.XM, l.YM)
  20126.         for i in range(4):
  20127.             s.foundations.append(_.Foundation_Class(x, y, _, i, dir = 2, mod = 13))
  20128.             x = x + l.XS
  20129.         
  20130.         for i in range(4):
  20131.             s.foundations.append(_.Foundation_Class(x, y, _, i, dir = 2, mod = 13, base_rank = 1))
  20132.             x = x + l.XS
  20133.         
  20134.         for i in range(2):
  20135.             (x, y) = (l.XM + (4, 3)[i] * l.XS, l.YM + (i + 1) * l.YS)
  20136.             for j in range((4, 5)[i]):
  20137.                 s.reserves.append(ReserveStack(x, y, _, max_accept = 0))
  20138.                 x = x + l.XS
  20139.             
  20140.         
  20141.         (x, y) = (l.XM, _.height - l.YS)
  20142.         s.talon = WasteTalonStack(x, y, _, max_rounds = 2)
  20143.         l.createText(s.talon, 'nn')
  20144.         x = x + l.XS
  20145.         s.waste = WasteStack(x, y, _)
  20146.         l.createText(s.waste, 'nn')
  20147.         l.defaultStackGroups()
  20148.  
  20149.     
  20150.     def startGame(_):
  20151.         _.startDealSample()
  20152.         _.s.talon.dealRow(rows = _.s.reserves)
  20153.         _.s.talon.dealCards()
  20154.  
  20155.  
  20156.  
  20157. class Kingdom(RoyalCotillion):
  20158.     Foundation_Class = RK_FoundationStack
  20159.     
  20160.     def createGame(_):
  20161.         (l, s) = (Layout(_), _.s)
  20162.         _.setSize(l.XM + 8 * l.XS, l.YM + 4 * l.YS)
  20163.         (x, y) = (l.XM, l.YM)
  20164.         for i in range(8):
  20165.             s.foundations.append(_.Foundation_Class(x, y, _, ANY_SUIT))
  20166.             x = x + l.XS
  20167.         
  20168.         (x, y) = (l.XM, y + l.YS)
  20169.         for i in range(8):
  20170.             s.reserves.append(ReserveStack(x, y, _, max_accept = 0))
  20171.             x = x + l.XS
  20172.         
  20173.         (x, y) = (l.XM + 3 * l.XS, y + 3 * l.YS / 2)
  20174.         s.talon = WasteTalonStack(x, y, _, max_rounds = 1)
  20175.         l.createText(s.talon, 'sw')
  20176.         x = x + l.XS
  20177.         s.waste = WasteStack(x, y, _)
  20178.         l.createText(s.waste, 'se')
  20179.         l.defaultStackGroups()
  20180.  
  20181.     
  20182.     def _shuffleHook(_, cards):
  20183.         return _._shuffleHookMoveToTop(cards, (lambda c: (c.rank == 0, c.suit)), 1)
  20184.  
  20185.     
  20186.     def startGame(_):
  20187.         _.startDealSample()
  20188.         _.s.talon.dealRow(rows = (_.s.foundations[0],))
  20189.         _.s.talon.dealRow(rows = _.s.reserves)
  20190.         _.s.talon.dealCards()
  20191.  
  20192.  
  20193.  
  20194. class Alhambra_Waste(WasteStack):
  20195.     
  20196.     def acceptsCards(_, from_stack, cards):
  20197.         if not WasteStack.acceptsCards(_, from_stack, cards):
  20198.             return 0
  20199.         
  20200.         if not (_.cards):
  20201.             return 0
  20202.         
  20203.         (c1, c2) = (_.cards[-1], cards[0])
  20204.         if not c1.suit == c2.suit and (c1.rank + 1) % _.cap.mod == c2.rank:
  20205.             pass
  20206.         return (c2.rank + 1) % _.cap.mod == c1.rank
  20207.  
  20208.  
  20209.  
  20210. class Alhambra(Game):
  20211.     
  20212.     def createGame(_):
  20213.         (l, s) = (Layout(_), _.s)
  20214.         _.setSize(l.XM + 8 * l.XS, l.YM + 4 * l.YS)
  20215.         (x, y) = (l.XM, l.YM)
  20216.         for i in range(4):
  20217.             s.foundations.append(SS_FoundationStack(x, y, _, suit = i, max_move = 0))
  20218.             x = x + l.XS
  20219.         
  20220.         for i in range(4):
  20221.             s.foundations.append(SS_FoundationStack(x, y, _, suit = i, max_move = 0, base_rank = KING, dir = -1))
  20222.             x = x + l.XS
  20223.         
  20224.         (x, y) = (l.XM, y + l.YS)
  20225.         for i in range(8):
  20226.             s.reserves.append(BasicRowStack(x, y, _, max_accept = 0))
  20227.             x = x + l.XS
  20228.         
  20229.         (x, y) = (l.XM + 3 * l.XS, y + 2 * l.YS)
  20230.         s.talon = WasteTalonStack(x, y, _, max_rounds = 3)
  20231.         l.createText(s.talon, 'sw')
  20232.         x = x + l.XS
  20233.         s.waste = Alhambra_Waste(x, y, _, mod = 13, max_accept = 1)
  20234.         l.createText(s.waste, 'se')
  20235.         s.rows.append(s.waste)
  20236.         l.defaultStackGroups()
  20237.  
  20238.     
  20239.     def _shuffleHook(_, cards):
  20240.         return _._shuffleHookMoveToTop(cards, (lambda c: if c.deck == 0:
  20241. pass(c.rank in (0, 12), (c.rank, c.suit))), 8)
  20242.  
  20243.     
  20244.     def startGame(_):
  20245.         _.s.talon.dealRow(rows = _.s.foundations, frames = 0)
  20246.         for i in range(3):
  20247.             _.s.talon.dealRow(rows = _.s.reserves, frames = 0)
  20248.         
  20249.         _.startDealSample()
  20250.         _.s.talon.dealRow(rows = _.s.reserves)
  20251.  
  20252.  
  20253.  
  20254. class Carpet(Game):
  20255.     Foundation_Class = SS_FoundationStack
  20256.     
  20257.     def createGame(_):
  20258.         (l, s) = (Layout(_), _.s)
  20259.         _.setSize(l.XM + 9 * l.XS, l.YM + 4 * l.YS)
  20260.         for i in range(4):
  20261.             for j in range(5):
  20262.                 (x, y) = (l.XM + (j + 3) * l.XS, l.YM + i * l.YS)
  20263.                 s.rows.append(ReserveStack(x, y, _))
  20264.             
  20265.         
  20266.         for i in range(4):
  20267.             (dx, dy) = ((2, 1), (8, 1), (2, 2), (8, 2))[i]
  20268.             (x, y) = (l.XM + dx * l.XS, l.YM + dy * l.YS)
  20269.             s.foundations.append(_.Foundation_Class(x, y, _, i))
  20270.         
  20271.         (x, y) = (l.XM, l.YM)
  20272.         s.talon = WasteTalonStack(x, y, _, max_rounds = 1)
  20273.         l.createText(s.talon, 'se')
  20274.         y = y + l.YS
  20275.         s.waste = WasteStack(x, y, _)
  20276.         l.createText(s.waste, 'se')
  20277.         l.defaultStackGroups()
  20278.  
  20279.     
  20280.     def _shuffleHook(_, cards):
  20281.         return _._shuffleHookMoveToTop(cards, (lambda c: (c.rank == 0, c.suit)))
  20282.  
  20283.     
  20284.     def startGame(_):
  20285.         _.startDealSample()
  20286.         _.s.talon.dealRow(rows = _.s.foundations)
  20287.         _.s.talon.dealRow()
  20288.         _.s.talon.dealCards()
  20289.  
  20290.  
  20291. registerGame(GameInfo(54, RoyalCotillion, 'Royal Cotillion', GI.GT_2DECK_TYPE, 2, 0))
  20292. registerGame(GameInfo(55, OddAndEven, 'Odd and Even', GI.GT_2DECK_TYPE, 2, 1))
  20293. registerGame(GameInfo(143, Kingdom, 'Kingdom', GI.GT_2DECK_TYPE, 2, 0))
  20294. registerGame(GameInfo(234, Alhambra, 'Alhambra', GI.GT_2DECK_TYPE, 2, 2))
  20295. registerGame(GameInfo(97, Carpet, 'Carpet', GI.GT_1DECK_TYPE, 1, 0))
  20296.  
  20297. class Osmosis_Foundation(AbstractFoundationStack):
  20298.     
  20299.     def acceptsCards(_, from_stack, cards):
  20300.         if not AbstractFoundationStack.acceptsCards(_, from_stack, cards):
  20301.             return 0
  20302.         
  20303.         if not __debug__ and len(cards) == 1:
  20304.             raise AssertionError
  20305.         (max_s, max_cards) = (None, -1)
  20306.         for s in _.game.s.foundations:
  20307.             pass
  20308.         
  20309.         if len(_.cards) < max_cards:
  20310.             if cards[0].rank != max_s.cards[len(_.cards)].rank:
  20311.                 return 0
  20312.             
  20313.         
  20314.         return 1
  20315.  
  20316.  
  20317.  
  20318. class Osmosis(Game):
  20319.     
  20320.     def createGame(_, max_rounds = -1, num_deal = 1):
  20321.         (l, s) = (Layout(_, XOFFSET = 12), _.s)
  20322.         _.setSize(l.XM + 7 * l.XS, l.YM + 4 * l.YS)
  20323.         (x, y) = (l.XM, l.YM)
  20324.         for i in range(4):
  20325.             stack = RK_RowStack(x, y, _, max_move = 1, max_accept = 0)
  20326.             (stack.CARD_XOFFSET, stack.CARD_YOFFSET) = (l.XOFFSET, 0)
  20327.             s.rows.append(stack)
  20328.             y = y + l.YS
  20329.         
  20330.         (x, y) = (l.XM + 2 * l.XS, l.YM)
  20331.         for i in range(4):
  20332.             stack = Osmosis_Foundation(x, y, _, i, base_rank = ANY_RANK, max_move = 0)
  20333.             (stack.CARD_XOFFSET, stack.CARD_YOFFSET) = (l.XOFFSET, 0)
  20334.             s.foundations.append(stack)
  20335.             y = y + l.YS
  20336.         
  20337.         (x, y) = (_.width - l.XS, l.YM + l.YS)
  20338.         s.talon = WasteTalonStack(x, y, _, max_rounds = max_rounds, num_deal = num_deal)
  20339.         l.createText(s.talon, 'sw')
  20340.         y = y + l.YS
  20341.         s.waste = WasteStack(x, y, _)
  20342.         l.createText(s.waste, 'sw')
  20343.         l.defaultStackGroups()
  20344.  
  20345.     
  20346.     def startGame(_, flip = 0):
  20347.         base_card = _.s.talon.getCard()
  20348.         n = base_card.suit * _.gameinfo.decks
  20349.         to_stack = _.s.foundations[n]
  20350.         _.startDealSample()
  20351.         _.flipMove(_.s.talon)
  20352.         _.moveMove(1, _.s.talon, to_stack)
  20353.         for i in range(3):
  20354.             _.s.talon.dealRow(flip = flip)
  20355.         
  20356.         _.s.talon.dealRow()
  20357.         _.s.talon.dealCards()
  20358.  
  20359.  
  20360.  
  20361. class Peek(Osmosis):
  20362.     
  20363.     def startGame(_):
  20364.         Osmosis.startGame(_, flip = 1)
  20365.  
  20366.  
  20367. registerGame(GameInfo(59, Osmosis, 'Osmosis', GI.GT_1DECK_TYPE, 1, -1))
  20368. registerGame(GameInfo(60, Peek, 'Peek', GI.GT_1DECK_TYPE, 1, -1))
  20369.  
  20370. class MonteCarlo_Hint(DefaultHint):
  20371.     pass
  20372.  
  20373.  
  20374. class MonteCarlo_Talon(TalonStack):
  20375.     
  20376.     def canDealCards(_):
  20377.         free = 0
  20378.         for r in _.game.s.rows:
  20379.             if not (r.cards):
  20380.                 free = 1
  20381.             elif free:
  20382.                 return 1
  20383.             
  20384.         
  20385.         if free:
  20386.             pass
  20387.         return len(_.cards)
  20388.  
  20389.     
  20390.     def dealCards(_, sound = 0):
  20391.         _.game.updateStackMove(_.game.s.talon, 2 | 16)
  20392.         n = _.game.fillEmptyStacks()
  20393.         _.game.updateStackMove(_.game.s.talon, 1 | 16)
  20394.         return n
  20395.  
  20396.  
  20397.  
  20398. class MonteCarlo_RowStack(BasicRowStack):
  20399.     
  20400.     def acceptsCards(_, from_stack, cards):
  20401.         if not OpenStack.acceptsCards(_, from_stack, cards):
  20402.             return 0
  20403.         
  20404.         if _.cards[-1].rank != cards[0].rank:
  20405.             return 0
  20406.         
  20407.         return _.game.isNeighbour(from_stack, _)
  20408.  
  20409.     
  20410.     def moveMove(_, ncards, to_stack, frames = -1, shadow = -1):
  20411.         if __debug__:
  20412.             if not ncards == 1 and to_stack in _.game.s.rows:
  20413.                 raise AssertionError
  20414.         if to_stack.cards:
  20415.             _._dropPairMove(ncards, to_stack, frames = -1, shadow = shadow)
  20416.         else:
  20417.             BasicRowStack.moveMove(_, ncards, to_stack, frames = frames, shadow = shadow)
  20418.  
  20419.     
  20420.     def _dropPairMove(_, n, other_stack, frames = -1, shadow = -1):
  20421.         game = _.game
  20422.         old_state = game.enterState(game.S_FILL)
  20423.         f = game.s.foundations[0]
  20424.         game.updateStackMove(game.s.talon, 2 | 16)
  20425.         if not (game.demo):
  20426.             game.playSample('droppair', priority = 200)
  20427.         
  20428.         game.moveMove(n, _, f, frames = frames, shadow = shadow)
  20429.         game.moveMove(n, other_stack, f, frames = frames, shadow = shadow)
  20430.         _.fillStack()
  20431.         other_stack.fillStack()
  20432.         if _.game.FILL_STACKS_AFTER_DROP:
  20433.             game.fillEmptyStacks()
  20434.         
  20435.         game.updateStackMove(game.s.talon, 1 | 16)
  20436.         game.leaveState(old_state)
  20437.  
  20438.  
  20439.  
  20440. class MonteCarlo(Game):
  20441.     Talon_Class = MonteCarlo_Talon
  20442.     Foundation_Class = StackWrapper(AbstractFoundationStack, max_accept = 0)
  20443.     RowStack_Class = MonteCarlo_RowStack
  20444.     Hint_Class = MonteCarlo_Hint
  20445.     FILL_STACKS_AFTER_DROP = 0
  20446.     
  20447.     def createGame(_):
  20448.         (l, s) = (Layout(_), _.s)
  20449.         _.setSize(l.XM + 6.5 * l.XS, l.YM + 5 * l.YS)
  20450.         for i in range(5):
  20451.             for j in range(5):
  20452.                 (x, y) = (l.XM + j * l.XS, l.YM + i * l.YS)
  20453.                 s.rows.append(_.RowStack_Class(x, y, _, max_accept = 1, max_cards = 2, dir = 0, base_rank = NO_RANK))
  20454.             
  20455.         
  20456.         (x, y) = (l.XM + 11 * l.XS / 2, l.YM)
  20457.         s.foundations.append(_.Foundation_Class(x, y, _, suit = ANY_SUIT, max_move = 0, max_cards = 52, base_rank = ANY_RANK))
  20458.         l.createText(s.foundations[0], 'ss')
  20459.         y = y + 2 * l.YS
  20460.         s.talon = _.Talon_Class(x, y, _, max_rounds = 1)
  20461.         l.createText(s.talon, 'ss', text_format = '%D')
  20462.         l.defaultStackGroups()
  20463.  
  20464.     
  20465.     def startGame(_):
  20466.         _.startDealSample()
  20467.         _.s.talon.dealRow()
  20468.  
  20469.     
  20470.     def getAutoStacks(_, event = None):
  20471.         return ((), (), _.sg.dropstacks)
  20472.  
  20473.     
  20474.     def shallHighlightMatch(_, stack1, card1, stack2, card2):
  20475.         return card1.rank == card2.rank
  20476.  
  20477.     
  20478.     def isNeighbour(_, stack1, stack2):
  20479.         if stack1.id <= stack1.id:
  20480.             pass
  20481.         elif stack1.id <= 24:
  20482.             pass
  20483.         if not None if stack2.id <= stack2.id else stack2.id <= 24:
  20484.             return 0
  20485.         
  20486.         column = stack2.id % 5
  20487.         diff = stack1.id - stack2.id
  20488.         if column == 0:
  20489.             return diff in (-5, -4, 1, 5, 6)
  20490.         elif column == 4:
  20491.             return diff in (-6, -5, -1, 4, 5)
  20492.         else:
  20493.             return diff in (-6, -5, -4, -1, 1, 4, 5, 6)
  20494.  
  20495.     
  20496.     def fillEmptyStacks(_):
  20497.         (free, n) = (0, 0)
  20498.         _.startDealSample()
  20499.         for r in _.s.rows:
  20500.             if not __debug__ and len(r.cards) <= 1:
  20501.                 raise AssertionError
  20502.             0
  20503.             if not (r.cards):
  20504.                 free = free + 1
  20505.             elif free > 0:
  20506.                 to_stack = _.allstacks[r.id - free]
  20507.                 _.moveMove(1, r, to_stack, frames = 4, shadow = 0)
  20508.             
  20509.         
  20510.         if free > 0:
  20511.             for r in _.s.rows:
  20512.                 if not (r.cards):
  20513.                     _.flipMove(_.s.talon)
  20514.                     _.moveMove(1, _.s.talon, r)
  20515.                     n = n + 1
  20516.                 
  20517.             
  20518.         
  20519.         _.stopSamples()
  20520.         return n
  20521.  
  20522.  
  20523.  
  20524. class Monaco(MonteCarlo):
  20525.     pass
  20526.  
  20527.  
  20528. class Weddings_Talon(MonteCarlo_Talon):
  20529.     
  20530.     def canDealCards(_):
  20531.         free = 0
  20532.         for r in _.game.s.rows:
  20533.             if not (r.cards):
  20534.                 free = 1
  20535.             else:
  20536.                 k = r.id
  20537.                 while k >= 5 and not (_.game.allstacks[k - 5].cards):
  20538.                     k = k - 5
  20539.                     continue
  20540.                     _.game.s.rows
  20541.                 if k != r.id:
  20542.                     return 1
  20543.                 
  20544.         
  20545.         if free:
  20546.             pass
  20547.         return len(_.cards)
  20548.  
  20549.  
  20550.  
  20551. class Weddings(MonteCarlo):
  20552.     Talon_Class = Weddings_Talon
  20553.     
  20554.     def fillEmptyStacks(_):
  20555.         (free, n) = (0, 0)
  20556.         _.startDealSample()
  20557.         for r in _.s.rows:
  20558.             if not __debug__ and len(r.cards) <= 1:
  20559.                 raise AssertionError
  20560.             0
  20561.             if not (r.cards):
  20562.                 free = free + 1
  20563.             else:
  20564.                 k = r.id
  20565.                 while k >= 5 and not (_.allstacks[k - 5].cards):
  20566.                     k = k - 5
  20567.                 if k != r.id:
  20568.                     to_stack = _.allstacks[k]
  20569.                     _.moveMove(1, r, to_stack, frames = 4, shadow = 0)
  20570.                 
  20571.         
  20572.         if free > 0:
  20573.             for r in _.s.rows:
  20574.                 if not (r.cards):
  20575.                     _.flipMove(_.s.talon)
  20576.                     _.moveMove(1, _.s.talon, r)
  20577.                     n = n + 1
  20578.                 
  20579.             
  20580.         
  20581.         _.stopSamples()
  20582.         return n
  20583.  
  20584.  
  20585.  
  20586. class SimpleCarlo(MonteCarlo):
  20587.     FILL_STACKS_AFTER_DROP = 1
  20588.     
  20589.     def getAutoStacks(_, event = None):
  20590.         return ((), (), ())
  20591.  
  20592.     
  20593.     def isNeighbour(_, stack1, stack2):
  20594.         if stack1.id <= stack1.id:
  20595.             pass
  20596.         elif stack1.id <= 24:
  20597.             pass
  20598.         return None if stack2.id <= stack2.id else stack2.id <= 24
  20599.  
  20600.  
  20601.  
  20602. class SimplePairs(MonteCarlo):
  20603.     
  20604.     def createGame(_):
  20605.         (l, s) = (Layout(_), _.s)
  20606.         _.setSize(l.XM + 6 * l.XS, l.YM + 4 * l.YS)
  20607.         for i in range(3):
  20608.             for j in range(3):
  20609.                 (x, y) = (l.XM + (2 * j + 3) * l.XS / 2, l.YM + (2 * i + 1) * l.YS / 2)
  20610.                 s.rows.append(_.RowStack_Class(x, y, _, max_accept = 1, max_cards = 2, dir = 0, base_rank = NO_RANK))
  20611.             
  20612.         
  20613.         (x, y) = (l.XM, l.YM + 3 * l.YS / 2)
  20614.         s.talon = TalonStack(x, y, _, max_rounds = 1)
  20615.         l.createText(s.talon, 'ss')
  20616.         x = x + 5 * l.XS
  20617.         s.foundations.append(_.Foundation_Class(x, y, _, suit = ANY_SUIT, max_move = 0, max_cards = 52, base_rank = ANY_RANK))
  20618.         l.createText(s.foundations[0], 'ss')
  20619.         l.defaultStackGroups()
  20620.  
  20621.     
  20622.     def fillStack(_, stack):
  20623.         if stack in _.s.rows:
  20624.             if len(stack.cards) == 0 and len(_.s.talon.cards) > 0:
  20625.                 _.flipMove(_.s.talon)
  20626.                 _.moveMove(1, _.s.talon, stack)
  20627.             
  20628.         
  20629.  
  20630.     
  20631.     def isNeighbour(_, stack1, stack2):
  20632.         if stack1.id <= stack1.id:
  20633.             pass
  20634.         elif stack1.id <= 15:
  20635.             pass
  20636.         return None if stack2.id <= stack2.id else stack2.id <= 15
  20637.  
  20638.  
  20639.  
  20640. class Neighbour_Foundation(AbstractFoundationStack):
  20641.     
  20642.     def acceptsCards(_, from_stack, cards):
  20643.         if not AbstractFoundationStack.acceptsCards(_, from_stack, cards):
  20644.             return 0
  20645.         
  20646.         return cards[0].rank == KING
  20647.  
  20648.  
  20649.  
  20650. class Neighbour_RowStack(MonteCarlo_RowStack):
  20651.     
  20652.     def acceptsCards(_, from_stack, cards):
  20653.         if not OpenStack.acceptsCards(_, from_stack, cards):
  20654.             return 0
  20655.         
  20656.         if _.cards[-1].rank + cards[0].rank != 11:
  20657.             return 0
  20658.         
  20659.         return _.game.isNeighbour(from_stack, _)
  20660.  
  20661.     
  20662.     def clickHandler(_, event):
  20663.         if _._dropKingClickHandler(event):
  20664.             return 1
  20665.         
  20666.         return MonteCarlo_RowStack.clickHandler(_, event)
  20667.  
  20668.     
  20669.     def moveMove(_, ncards, to_stack, frames = -1, shadow = -1):
  20670.         if not __debug__ and ncards == 1:
  20671.             raise AssertionError
  20672.         if _.cards[-1].rank == KING:
  20673.             if not __debug__ and to_stack in _.game.s.foundations:
  20674.                 raise AssertionError
  20675.             BasicRowStack.moveMove(_, ncards, to_stack, frames = frames, shadow = shadow)
  20676.         else:
  20677.             MonteCarlo_RowStack.moveMove(_, ncards, to_stack, frames = frames, shadow = shadow)
  20678.  
  20679.     
  20680.     def _dropKingClickHandler(_, event):
  20681.         if not (_.cards):
  20682.             return 0
  20683.         
  20684.         c = _.cards[-1]
  20685.         if c.face_up and c.rank == KING and not _.basicIsBlocked():
  20686.             _.game.playSample('autodrop', priority = 20)
  20687.             _.playMoveMove(1, _.game.s.foundations[0], sound = 0)
  20688.             return 1
  20689.         
  20690.         return 0
  20691.  
  20692.     
  20693.     def fillStack(_):
  20694.         if not (_.cards) and _.game.s.talon.canDealCards():
  20695.             old_state = _.game.enterState(_.game.S_FILL)
  20696.             _.game.s.talon.dealCards()
  20697.             _.game.leaveState(old_state)
  20698.         
  20699.  
  20700.  
  20701.  
  20702. class Neighbour(MonteCarlo):
  20703.     Foundation_Class = Neighbour_Foundation
  20704.     RowStack_Class = Neighbour_RowStack
  20705.     FILL_STACKS_AFTER_DROP = 1
  20706.     
  20707.     def getAutoStacks(_, event = None):
  20708.         return ((), _.sg.dropstacks, ())
  20709.  
  20710.     
  20711.     def shallHighlightMatch(_, stack1, card1, stack2, card2):
  20712.         return card1.rank + card2.rank == 11
  20713.  
  20714.  
  20715.  
  20716. class Fourteen_RowStack(MonteCarlo_RowStack):
  20717.     
  20718.     def acceptsCards(_, from_stack, cards):
  20719.         if not OpenStack.acceptsCards(_, from_stack, cards):
  20720.             return 0
  20721.         
  20722.         return _.cards[-1].rank + cards[0].rank == 12
  20723.  
  20724.  
  20725.  
  20726. class Fourteen(Game):
  20727.     Foundation_Class = StackWrapper(AbstractFoundationStack, max_accept = 0)
  20728.     RowStack_Class = Fourteen_RowStack
  20729.     FILL_STACKS_AFTER_DROP = 0
  20730.     
  20731.     def createGame(_):
  20732.         (l, s) = (Layout(_), _.s)
  20733.         _.setSize(l.XM + 7 * l.XS, l.YM + 5 * l.YS)
  20734.         for i in (0, 2.5):
  20735.             for j in range(6):
  20736.                 (x, y) = (l.XM + j * l.XS, l.YM + i * l.YS)
  20737.                 s.rows.append(_.RowStack_Class(x, y, _, max_move = 1, max_accept = 1, dir = 0, base_rank = NO_RANK))
  20738.             
  20739.         
  20740.         (x, y) = (l.XM + 6 * l.XS, l.YM)
  20741.         s.foundations.append(_.Foundation_Class(x, y, _, suit = ANY_SUIT, max_move = 0, max_cards = 52, base_rank = ANY_RANK))
  20742.         l.createText(s.foundations[0], 'ss')
  20743.         (x, y) = (_.width - l.XS, _.height - l.YS)
  20744.         s.talon = InitialDealTalonStack(x, y, _)
  20745.         l.defaultStackGroups()
  20746.  
  20747.     
  20748.     def startGame(_):
  20749.         for i in range(3):
  20750.             _.s.talon.dealRow(frames = 0)
  20751.         
  20752.         _.startDealSample()
  20753.         _.s.talon.dealRow()
  20754.         _.s.talon.dealRow(rows = _.s.rows[:4])
  20755.  
  20756.     
  20757.     def getAutoStacks(_, event = None):
  20758.         return ((), (), _.sg.dropstacks)
  20759.  
  20760.     
  20761.     def shallHighlightMatch(_, stack1, card1, stack2, card2):
  20762.         return card1.rank + card2.rank == 12
  20763.  
  20764.  
  20765.  
  20766. class Nestor_RowStack(MonteCarlo_RowStack):
  20767.     
  20768.     def acceptsCards(_, from_stack, cards):
  20769.         if not OpenStack.acceptsCards(_, from_stack, cards):
  20770.             return 0
  20771.         
  20772.         return _.cards[-1].rank == cards[0].rank
  20773.  
  20774.  
  20775.  
  20776. class Nestor(Game):
  20777.     Foundation_Class = StackWrapper(AbstractFoundationStack, max_accept = 0)
  20778.     RowStack_Class = Nestor_RowStack
  20779.     FILL_STACKS_AFTER_DROP = 0
  20780.     
  20781.     def createGame(_):
  20782.         (l, s) = (Layout(_), _.s)
  20783.         _.setSize(l.XM + 8 * l.XS, l.YM + 4 * l.YS)
  20784.         for j in range(8):
  20785.             (x, y) = (l.XM + j * l.XS, l.YM)
  20786.             s.rows.append(_.RowStack_Class(x, y, _, max_move = 1, max_accept = 1, dir = 0, base_rank = NO_RANK))
  20787.         
  20788.         for j in range(4):
  20789.             (x, y) = (l.XM + (j + 2) * l.XS, l.YM + 3 * l.YS)
  20790.             s.rows.append(_.RowStack_Class(x, y, _, max_move = 1, max_accept = 1, dir = 0, base_rank = NO_RANK))
  20791.         
  20792.         (x, y) = (_.width - l.XS, _.height - l.YS)
  20793.         s.foundations.append(_.Foundation_Class(x, y, _, suit = ANY_SUIT, max_move = 0, max_cards = 52, base_rank = ANY_RANK))
  20794.         l.createText(s.foundations[0], 'nn')
  20795.         (x, y) = (l.XM, _.height - l.YS)
  20796.         s.talon = InitialDealTalonStack(x, y, _)
  20797.         l.defaultStackGroups()
  20798.  
  20799.     
  20800.     def _shuffleHook(_, cards):
  20801.         return cards
  20802.  
  20803.     
  20804.     def startGame(_):
  20805.         for i in range(5):
  20806.             _.s.talon.dealRow(rows = _.s.rows[:8], frames = 0)
  20807.         
  20808.         _.startDealSample()
  20809.         _.s.talon.dealRow()
  20810.  
  20811.     
  20812.     def getAutoStacks(_, event = None):
  20813.         return ((), (), _.sg.dropstacks)
  20814.  
  20815.     
  20816.     def shallHighlightMatch(_, stack1, card1, stack2, card2):
  20817.         return card1.rank == card2.rank
  20818.  
  20819.  
  20820.  
  20821. class DerLetzteMonarch_Foundation(SS_FoundationStack):
  20822.     
  20823.     def acceptsCards(_, from_stack, cards):
  20824.         if cards is None:
  20825.             return SS_FoundationStack.acceptsCards(_, from_stack, from_stack.cards)
  20826.         
  20827.         if not SS_FoundationStack.acceptsCards(_, from_stack, cards):
  20828.             return 0
  20829.         
  20830.         return from_stack in _.game.s.reserves
  20831.  
  20832.  
  20833.  
  20834. class DerLetzteMonarch_RowStack(ReserveStack):
  20835.     
  20836.     def canDropCards(_, stacks):
  20837.         return (None, 0)
  20838.  
  20839.     
  20840.     def acceptsCards(_, from_stack, cards):
  20841.         if not ReserveStack.acceptsCards(_, from_stack, cards):
  20842.             return 0
  20843.         
  20844.         if not _.game.isNeighbour(from_stack, _):
  20845.             return 0
  20846.         
  20847.         return _._getDropStack() is not None
  20848.  
  20849.     
  20850.     def _getDropStack(_):
  20851.         if len(_.cards) != 1:
  20852.             return None
  20853.         
  20854.         for s in _.game.s.foundations:
  20855.             pass
  20856.         
  20857.         for s in _.game.s.reserves:
  20858.             pass
  20859.         
  20860.         return None
  20861.  
  20862.     
  20863.     def moveMove(_, ncards, to_stack, frames = -1, shadow = -1):
  20864.         if __debug__:
  20865.             if not ncards == 1 and to_stack in _.game.s.rows:
  20866.                 raise AssertionError
  20867.         if not __debug__ and len(to_stack.cards) == 1:
  20868.             raise AssertionError
  20869.         _._handlePairMove(ncards, to_stack, frames = -1, shadow = 0)
  20870.  
  20871.     
  20872.     def _handlePairMove(_, n, other_stack, frames = -1, shadow = -1):
  20873.         game = _.game
  20874.         old_state = game.enterState(game.S_FILL)
  20875.         s = other_stack._getDropStack()
  20876.         if not __debug__ and s is not None:
  20877.             raise AssertionError
  20878.         game.moveMove(n, other_stack, s, frames = frames, shadow = shadow)
  20879.         game.moveMove(n, _, other_stack, frames = 0)
  20880.         game.leaveState(old_state)
  20881.  
  20882.  
  20883.  
  20884. class DerLetzteMonarch_ReserveStack(ReserveStack):
  20885.     
  20886.     def clickHandler(_, event):
  20887.         return _.doubleclickHandler(event)
  20888.  
  20889.  
  20890.  
  20891. class DerLetzteMonarch(Game):
  20892.     
  20893.     def createGame(_):
  20894.         (l, s) = (Layout(_, XM = 4), _.s)
  20895.         _.setSize(l.XM + 13 * l.XS, l.YM + 5 * l.YS)
  20896.         for i in range(4):
  20897.             for j in range(13):
  20898.                 (x, y) = (l.XM + j * l.XS, l.YM + (i + 1) * l.YS)
  20899.                 s.rows.append(DerLetzteMonarch_RowStack(x, y, _, max_accept = 1, max_cards = 2))
  20900.             
  20901.         
  20902.         for i in range(4):
  20903.             (x, y) = (l.XM + (i + 2) * l.XS, l.YM)
  20904.             s.reserves.append(DerLetzteMonarch_ReserveStack(x, y, _, max_accept = 0))
  20905.         
  20906.         for i in range(4):
  20907.             (x, y) = (l.XM + (i + 7) * l.XS, l.YM)
  20908.             s.foundations.append(DerLetzteMonarch_Foundation(x, y, _, i, max_move = 0))
  20909.         
  20910.         s.talon = InitialDealTalonStack(l.XM, l.YM, _)
  20911.         _.sg.talonstacks = [
  20912.             s.talon]
  20913.         _.sg.openstacks = s.foundations + s.rows
  20914.         _.sg.dropstacks = s.rows + s.reserves
  20915.         _.sg.reservestacks = s.reserves
  20916.  
  20917.     
  20918.     def startGame(_):
  20919.         _.s.talon.dealRow(rows = _.s.rows[:39], frames = 0)
  20920.         _.startDealSample()
  20921.         _.s.talon.dealRow(rows = _.s.rows[39:])
  20922.  
  20923.     
  20924.     def isGameWon(_):
  20925.         c = 0
  20926.         for s in _.s.foundations:
  20927.             c = c + len(s.cards)
  20928.         
  20929.         return c == 51
  20930.  
  20931.     
  20932.     def getAutoStacks(_, event = None):
  20933.         return ((), _.s.reserves, ())
  20934.  
  20935.     
  20936.     def getDemoInfoText(_):
  20937.         return 'Der letzte\nMonarch'
  20938.  
  20939.     
  20940.     def isNeighbour(_, stack1, stack2):
  20941.         if stack1.id <= stack1.id:
  20942.             pass
  20943.         elif stack1.id <= 51:
  20944.             pass
  20945.         if not None if stack2.id <= stack2.id else stack2.id <= 51:
  20946.             return 0
  20947.         
  20948.         column = stack2.id % 13
  20949.         diff = stack1.id - stack2.id
  20950.         if column == 0:
  20951.             return diff in (-13, 1, 13)
  20952.         elif column == 12:
  20953.             return diff in (-13, -1, 13)
  20954.         else:
  20955.             return diff in (-13, -1, 1, 13)
  20956.  
  20957.  
  20958. registerGame(GameInfo(89, MonteCarlo, 'Monte Carlo', GI.GT_PAIRING_TYPE, 1, 0))
  20959. registerGame(GameInfo(216, Monaco, 'Monaco', GI.GT_PAIRING_TYPE, 2, 0))
  20960. registerGame(GameInfo(212, Weddings, 'Weddings', GI.GT_PAIRING_TYPE, 1, 0))
  20961. registerGame(GameInfo(90, SimpleCarlo, 'Simple Carlo', GI.GT_PAIRING_TYPE, 1, 0))
  20962. registerGame(GameInfo(91, SimplePairs, 'Simple Pairs', GI.GT_PAIRING_TYPE, 1, 0))
  20963. registerGame(GameInfo(92, Neighbour, 'Neighbour', GI.GT_PAIRING_TYPE, 1, 0))
  20964. registerGame(GameInfo(96, Fourteen, 'Fourteen', GI.GT_PAIRING_TYPE | GI.GT_OPEN, 1, 0))
  20965. registerGame(GameInfo(235, Nestor, 'Nestor', GI.GT_PAIRING_TYPE | GI.GT_OPEN, 1, 0))
  20966. registerGame(GameInfo(152, DerLetzteMonarch, 'Der letzte Monarch', GI.GT_1DECK_TYPE | GI.GT_OPEN, 1, 0))
  20967.  
  20968. class RoyalEast(Game):
  20969.     Hint_Class = CautiousDefaultHint
  20970.     
  20971.     def createGame(_):
  20972.         (l, s) = (Layout(_), _.s)
  20973.         _.setSize(l.XM + 5.5 * l.XS, l.YM + 4 * l.YS)
  20974.         _.base_card = None
  20975.         for i in range(4):
  20976.             (dx, dy) = ((0, 0), (2, 0), (0, 2), (2, 2))[i]
  20977.             (x, y) = (l.XM + (2 * dx + 5) * l.XS / 2, l.YM + (2 * dy + 1) * l.YS / 2)
  20978.             stack = SS_FoundationStack(x, y, _, i, mod = 13, max_move = 0)
  20979.             stack.CARD_YOFFSET = 0
  20980.             s.foundations.append(stack)
  20981.         
  20982.         for i in range(5):
  20983.             (dx, dy) = ((1, 0), (0, 1), (1, 1), (2, 1), (1, 2))[i]
  20984.             (x, y) = (l.XM + (2 * dx + 5) * l.XS / 2, l.YM + (2 * dy + 1) * l.YS / 2)
  20985.             stack = RK_RowStack(x, y, _, mod = 13, max_move = 1)
  20986.             stack.CARD_YOFFSET = 0
  20987.             s.rows.append(stack)
  20988.         
  20989.         (x, y) = (l.XM, l.YM + 3 * l.YS / 2)
  20990.         s.talon = WasteTalonStack(x, y, _, max_rounds = 1)
  20991.         l.createText(s.talon, 'ss')
  20992.         x = x + l.XS
  20993.         s.waste = WasteStack(x, y, _)
  20994.         l.createText(s.waste, 'ss')
  20995.         l.defaultStackGroups()
  20996.  
  20997.     
  20998.     def startGame(_):
  20999.         _.base_card = _.s.talon.cards[-1]
  21000.         for s in _.s.foundations:
  21001.             s.cap.base_rank = _.base_card.rank
  21002.         
  21003.         c = _.s.talon.getCard()
  21004.         to_stack = _.s.foundations[c.suit * _.gameinfo.decks]
  21005.         _.flipMove(_.s.talon)
  21006.         _.moveMove(1, _.s.talon, to_stack, frames = 0)
  21007.         _.startDealSample()
  21008.         _.s.talon.dealRow()
  21009.         _.s.talon.dealCards()
  21010.  
  21011.     
  21012.     def _restoreGameHook(_, game):
  21013.         _.base_card = _.cards[game.loadinfo.base_card_id]
  21014.         for s in _.s.foundations:
  21015.             s.cap.base_rank = _.base_card.rank
  21016.         
  21017.  
  21018.     
  21019.     def _loadGameHook(_, p):
  21020.         _.loadinfo.addattr(base_card_id = None)
  21021.         _.loadinfo.base_card_id = p.load()
  21022.  
  21023.     
  21024.     def _saveGameHook(_, p):
  21025.         p.dump(_.base_card.id)
  21026.  
  21027.  
  21028. registerGame(GameInfo(93, RoyalEast, 'Royal East', GI.GT_1DECK_TYPE, 1, 0))
  21029.  
  21030. class TamOShanter(Game):
  21031.     
  21032.     def createGame(_):
  21033.         (l, s) = (Layout(_), _.s)
  21034.         _.setSize(l.XM + 6 * l.XS, l.YM + 4 * l.YS)
  21035.         (x, y) = (l.XM, l.YM)
  21036.         s.talon = DealRowTalonStack(x, y, _, max_rounds = 1)
  21037.         l.createText(s.talon, 'ss')
  21038.         for i in range(4):
  21039.             (x, y) = (l.XM + (i + 2) * l.XS, l.YM)
  21040.             s.foundations.append(RK_FoundationStack(x, y, _))
  21041.         
  21042.         for i in range(4):
  21043.             (x, y) = (l.XM + (i + 2) * l.XS, l.YM + l.YS)
  21044.             s.rows.append(BasicRowStack(x, y, _, max_move = 1, max_accept = 0))
  21045.             x = x + l.XS
  21046.         
  21047.         l.defaultStackGroups()
  21048.  
  21049.     
  21050.     def startGame(_):
  21051.         _.startDealSample()
  21052.         _.s.talon.dealRow()
  21053.  
  21054.     
  21055.     def getAutoStacks(_, event = None):
  21056.         return ((), (), _.sg.dropstacks)
  21057.  
  21058.  
  21059.  
  21060. class AuldLangSyne(TamOShanter):
  21061.     
  21062.     def _shuffleHook(_, cards):
  21063.         return _._shuffleHookMoveToTop(cards, (lambda c: (c.rank == 0, c.suit)))
  21064.  
  21065.     
  21066.     def startGame(_):
  21067.         _.s.talon.dealRow(rows = _.s.foundations, frames = 0)
  21068.         _.startDealSample()
  21069.         _.s.talon.dealRow()
  21070.  
  21071.  
  21072.  
  21073. class Strategy_Foundation(SS_FoundationStack):
  21074.     
  21075.     def acceptsCards(_, from_stack, cards):
  21076.         if not SS_FoundationStack.acceptsCards(_, from_stack, cards):
  21077.             return 0
  21078.         
  21079.         return len(_.game.s.talon.cards) == 0
  21080.  
  21081.  
  21082.  
  21083. class Strategy_RowStack(BasicRowStack):
  21084.     
  21085.     def acceptsCards(_, from_stack, cards):
  21086.         if not BasicRowStack.acceptsCards(_, from_stack, cards):
  21087.             return 0
  21088.         
  21089.         if from_stack is _.game.s.talon:
  21090.             pass
  21091.         return len(cards) == 1
  21092.  
  21093.     
  21094.     def canMoveCards(_, cards):
  21095.         if _.game.s.talon.cards:
  21096.             return 0
  21097.         
  21098.         return BasicRowStack.canMoveCards(_, cards)
  21099.  
  21100.     
  21101.     def clickHandler(_, event):
  21102.         if _.game.s.talon.cards:
  21103.             _.game.s.talon.playMoveMove(1, _)
  21104.             return 1
  21105.         
  21106.         return BasicRowStack.clickHandler(_, event)
  21107.  
  21108.     
  21109.     def doubleclickHandler(_, event):
  21110.         if _.game.s.talon.cards:
  21111.             _.game.s.talon.playMoveMove(1, _)
  21112.             return 1
  21113.         
  21114.         return BasicRowStack.doubleclickHandler(_, event)
  21115.  
  21116.     
  21117.     def getBottomImage(_):
  21118.         return _.game.app.images.getReserveBottom()
  21119.  
  21120.  
  21121.  
  21122. class Strategy(Game):
  21123.     
  21124.     def createGame(_):
  21125.         (l, s) = (Layout(_), _.s)
  21126.         _.setSize(l.XM + 8 * l.XS, l.YM + 4 * l.YS)
  21127.         (x, y) = (l.XM, l.YM)
  21128.         s.talon = OpenTalonStack(x, y, _)
  21129.         l.createText(s.talon, 'se')
  21130.         for i in range(4):
  21131.             (x, y) = (l.XM + (i + 2) * l.XS, l.YM)
  21132.             s.foundations.append(Strategy_Foundation(x, y, _, suit = i, max_move = 0))
  21133.         
  21134.         for i in range(8):
  21135.             (x, y) = (l.XM + i * l.XS, l.YM + l.YS)
  21136.             s.rows.append(Strategy_RowStack(x, y, _, max_move = 1, max_accept = 1))
  21137.             x = x + l.XS
  21138.         
  21139.         l.defaultStackGroups()
  21140.  
  21141.     
  21142.     def _shuffleHook(_, cards):
  21143.         return _._shuffleHookMoveToTop(cards, (lambda c: (c.rank == 0, c.suit)))
  21144.  
  21145.     
  21146.     def startGame(_):
  21147.         _.startDealSample()
  21148.         _.s.talon.dealRow(rows = _.s.foundations)
  21149.         _.s.talon.fillStack()
  21150.  
  21151.  
  21152.  
  21153. class Interregnum_Foundation(RK_FoundationStack):
  21154.     
  21155.     def acceptsCards(_, from_stack, cards):
  21156.         if not RK_FoundationStack.acceptsCards(_, from_stack, cards):
  21157.             return 0
  21158.         
  21159.         if len(_.cards) == 12:
  21160.             return from_stack.id == _.id - 8
  21161.         else:
  21162.             return from_stack in _.game.s.rows
  21163.  
  21164.  
  21165.  
  21166. class Interregnum(Game):
  21167.     GAME_VERSION = 2
  21168.     
  21169.     def createGame(_, rows = 8):
  21170.         (l, s) = (Layout(_), _.s)
  21171.         _.setSize(l.XM + max(9, rows) * l.XS, l.YM + 5 * l.YS)
  21172.         _.base_cards = None
  21173.         for i in range(8):
  21174.             (x, y) = (l.XM + i * l.XS, l.YM)
  21175.             s.reserves.append(ReserveStack(x, y, _, max_accept = 0))
  21176.         
  21177.         for i in range(8):
  21178.             (x, y) = (l.XM + i * l.XS, l.YM + l.YS)
  21179.             s.foundations.append(Interregnum_Foundation(x, y, _, mod = 13, max_move = 0))
  21180.         
  21181.         for i in range(rows):
  21182.             (x, y) = (l.XM + (2 * i + 8 - rows) * l.XS / 2, l.YM + 2 * l.YS)
  21183.             s.rows.append(RK_RowStack(x, y, _, max_accept = 0, max_move = 1))
  21184.         
  21185.         s.talon = DealRowTalonStack(_.width - l.XS, _.height - l.YS, _)
  21186.         l.createText(s.talon, 'nn')
  21187.         l.defaultStackGroups()
  21188.  
  21189.     
  21190.     def startGame(_):
  21191.         _.startDealSample()
  21192.         _.base_cards = []
  21193.         for i in range(8):
  21194.             _.base_cards.append(_.s.talon.getCard())
  21195.             _.s.foundations[i].cap.base_rank = (_.base_cards[i].rank + 1) % 13
  21196.             _.flipMove(_.s.talon)
  21197.             _.moveMove(1, _.s.talon, _.s.reserves[i])
  21198.         
  21199.         _.s.talon.dealRow()
  21200.  
  21201.     
  21202.     def getAutoStacks(_, event = None):
  21203.         return ((), (), _.sg.dropstacks)
  21204.  
  21205.     
  21206.     def shallHighlightMatch(_, stack1, card1, stack2, card2):
  21207.         if not (card1.rank + 1) % 13 == card2.rank:
  21208.             pass
  21209.         return (card2.rank + 1) % 13 == card1.rank
  21210.  
  21211.     
  21212.     def _restoreGameHook(_, game):
  21213.         _.base_cards = [
  21214.             None] * 8
  21215.         for i in range(8):
  21216.             id = game.loadinfo.base_card_ids[i]
  21217.             _.base_cards[i] = _.cards[id]
  21218.             _.s.foundations[i].cap.base_rank = (_.base_cards[i].rank + 1) % 13
  21219.         
  21220.  
  21221.     
  21222.     def _loadGameHook(_, p):
  21223.         ids = []
  21224.         for i in range(8):
  21225.             ids.append(p.load())
  21226.         
  21227.         _.loadinfo.addattr(base_card_ids = ids)
  21228.  
  21229.     
  21230.     def _saveGameHook(_, p):
  21231.         for c in _.base_cards:
  21232.             p.dump(c.id)
  21233.         
  21234.  
  21235.  
  21236. registerGame(GameInfo(172, TamOShanter, "Tam O'Shanter", GI.GT_NUMERICA, 1, 0))
  21237. registerGame(GameInfo(95, AuldLangSyne, 'Auld Lang Syne', GI.GT_NUMERICA, 1, 0))
  21238. registerGame(GameInfo(173, Strategy, 'Strategy', GI.GT_NUMERICA, 1, 0))
  21239. registerGame(GameInfo(123, Interregnum, 'Interregnum', GI.GT_NUMERICA, 2, 0))
  21240.  
  21241. class Doublets_Foundation(AbstractFoundationStack):
  21242.     
  21243.     def acceptsCards(_, from_stack, cards):
  21244.         if not AbstractFoundationStack.acceptsCards(_, from_stack, cards):
  21245.             return 0
  21246.         
  21247.         if _.cards:
  21248.             if (2 * _.cards[-1].rank + 1) % _.cap.mod != cards[0].rank:
  21249.                 return 0
  21250.             
  21251.         
  21252.         return 1
  21253.  
  21254.  
  21255.  
  21256. class Doublets(Game):
  21257.     Hint_Class = CautiousDefaultHint
  21258.     
  21259.     def createGame(_):
  21260.         (l, s) = (Layout(_), _.s)
  21261.         _.setSize(l.XM + 5.5 * l.XS, l.YM + 4 * l.YS)
  21262.         for dx, dy in ((0, 0), (1, 0), (2, 0), (0, 1), (2, 1), (0, 2), (2, 2)):
  21263.             (x, y) = (l.XM + (2 * dx + 5) * l.XS / 2, l.YM + (2 * dy + 1) * l.YS / 2)
  21264.             s.rows.append(ReserveStack(x, y, _))
  21265.         
  21266.         (dx, dy) = (1, 2)
  21267.         (x, y) = (l.XM + (2 * dx + 5) * l.XS / 2, l.YM + (2 * dy + 1) * l.YS / 2)
  21268.         s.foundations.append(Doublets_Foundation(x, y, _, ANY_SUIT, dir = 0, mod = 13, max_move = 0, max_cards = 48))
  21269.         l.createText(s.foundations[0], 'ss')
  21270.         (x, y) = (l.XM, l.YM + 3 * l.YS / 2)
  21271.         s.talon = WasteTalonStack(x, y, _, max_rounds = 3)
  21272.         l.createText(s.talon, 'ss')
  21273.         x = x + l.XS
  21274.         s.waste = WasteStack(x, y, _)
  21275.         l.createText(s.waste, 'ss')
  21276.         l.defaultStackGroups()
  21277.  
  21278.     
  21279.     def _shuffleHook(_, cards):
  21280.         (kings, topcards) = ([], [])
  21281.         for c in cards[:]:
  21282.             cards.remove(c)
  21283.             if c.rank == KING:
  21284.                 kings.append(c)
  21285.             else:
  21286.                 topcards.append(c)
  21287.                 if len(topcards) == 8:
  21288.                     break
  21289.                 
  21290.         
  21291.         return kings + cards + topcards
  21292.  
  21293.     
  21294.     def startGame(_):
  21295.         _.startDealSample()
  21296.         _.s.talon.dealRow()
  21297.         _.s.talon.dealRow(rows = _.s.foundations)
  21298.         _.s.talon.dealCards()
  21299.  
  21300.     
  21301.     def isGameWon(_):
  21302.         if _.s.talon.cards or _.s.waste.cards:
  21303.             return 0
  21304.         
  21305.         return len(_.s.foundations[0].cards) == 48
  21306.  
  21307.     
  21308.     def fillStack(_, stack):
  21309.         if stack in _.s.rows and not (stack.cards):
  21310.             old_state = _.enterState(_.S_FILL)
  21311.             if _.s.waste.cards:
  21312.                 _.s.waste.moveMove(1, stack)
  21313.             elif _.s.talon.canDealCards():
  21314.                 _.s.talon.dealCards()
  21315.                 _.s.waste.moveMove(1, stack)
  21316.             
  21317.             _.leaveState(old_state)
  21318.         
  21319.  
  21320.     
  21321.     def getAutoStacks(_, event = None):
  21322.         return ((), (), _.sg.dropstacks)
  21323.  
  21324.  
  21325. registerGame(GameInfo(111, Doublets, 'Doublets', GI.GT_1DECK_TYPE, 1, 2))
  21326.  
  21327. class SiebenBisAs_Hint(CautiousDefaultHint):
  21328.     
  21329.     def computeHints(_):
  21330.         game = _.game
  21331.         freerows = filter((lambda s: not (s.cards)), game.s.rows)
  21332.         for r in game.sg.dropstacks:
  21333.             if __debug__:
  21334.                 if not len(r.cards) == 1 and r.cards[-1].face_up:
  21335.                     raise AssertionError
  21336.             None if not (r.cards) else game.sg.dropstacks
  21337.             (c, pile, rpile) = (r.cards[0], r.cards, [])
  21338.             (t, ncards) = r.canDropCards(_.game.s.foundations)
  21339.             if t:
  21340.                 (score, color) = (0, None)
  21341.                 (score, color) = _._getDropCardScore(score, color, r, t, ncards)
  21342.                 _.addHint(score, ncards, r, t, color)
  21343.             
  21344.             for t in freerows:
  21345.                 pass
  21346.             
  21347.         
  21348.  
  21349.  
  21350.  
  21351. class SiebenBisAs_Foundation(SS_FoundationStack):
  21352.     
  21353.     def acceptsCards(_, from_stack, cards):
  21354.         if not SS_FoundationStack.acceptsCards(_, from_stack, cards):
  21355.             return 0
  21356.         
  21357.         if not (from_stack in _.game.s.rows):
  21358.             return 0
  21359.         
  21360.         if from_stack.id % 10 == 0:
  21361.             return 0
  21362.         
  21363.         return len(_.game.s.rows[from_stack.id - 1].cards) == 0
  21364.  
  21365.  
  21366.  
  21367. class SiebenBisAs_RowStack(BasicRowStack):
  21368.     
  21369.     def acceptsCards(_, from_stack, cards):
  21370.         if not BasicRowStack.acceptsCards(_, from_stack, cards):
  21371.             return 0
  21372.         
  21373.         if _.id % 10 != 0:
  21374.             s = _.game.s.rows[_.id - 1]
  21375.             if s.cards and s.cards[-1].suit == cards[0].suit and (s.cards[-1].rank + 1) % 13 == cards[0].rank:
  21376.                 return 1
  21377.             
  21378.         
  21379.         if _.id % 10 != 10 - 1:
  21380.             s = _.game.s.rows[_.id + 1]
  21381.             if s.cards and s.cards[-1].suit == cards[0].suit and (s.cards[-1].rank - 1) % 13 == cards[0].rank:
  21382.                 return 1
  21383.             
  21384.         
  21385.         return 0
  21386.  
  21387.     
  21388.     def getBottomImage(_):
  21389.         return _.game.app.images.getReserveBottom()
  21390.  
  21391.  
  21392.  
  21393. class SiebenBisAs(Game):
  21394.     Hint_Class = SiebenBisAs_Hint
  21395.     
  21396.     def createGame(_):
  21397.         (l, s) = (Layout(_), _.s)
  21398.         _.setSize(l.XM + 10 * l.XS, l.YM + 5 * l.YS)
  21399.         for i in range(3):
  21400.             for j in range(10):
  21401.                 (x, y) = (l.XM + j * l.XS, l.YM + (i + 1) * l.YS)
  21402.                 s.rows.append(SiebenBisAs_RowStack(x, y, _, max_accept = 1, max_cards = 1))
  21403.             
  21404.         
  21405.         for i in range(2):
  21406.             (x, y) = (l.XM + (i + 4) * l.XS, l.YM)
  21407.             s.reserves.append(ReserveStack(x, y, _, max_accept = 0))
  21408.         
  21409.         for i in range(4):
  21410.             (x, y) = (l.XM + (i + 3) * l.XS, l.YM + 4 * l.YS)
  21411.             s.foundations.append(SiebenBisAs_Foundation(x, y, _, i, base_rank = 6, mod = 13, max_move = 0))
  21412.         
  21413.         s.talon = InitialDealTalonStack(l.XM, _.height - l.YS, _)
  21414.         l.defaultStackGroups()
  21415.  
  21416.     
  21417.     def startGame(_):
  21418.         _.startDealSample()
  21419.         _.s.talon.dealRow()
  21420.         _.s.talon.dealRow(rows = _.s.reserves)
  21421.         stacks = filter((lambda r: r.cards[-1].rank == 6), _.s.rows)
  21422.         for r in stacks:
  21423.             _.moveMove(1, r, _.s.foundations[r.cards[-1].suit])
  21424.         
  21425.  
  21426.  
  21427.  
  21428. class Maze_RowStack(BasicRowStack):
  21429.     
  21430.     def acceptsCards(_, from_stack, cards):
  21431.         if not BasicRowStack.acceptsCards(_, from_stack, cards):
  21432.             return 0
  21433.         
  21434.         s = _.game.s.rows[(_.id - 1) % 54]
  21435.         if s.cards:
  21436.             if s.cards[-1].suit == cards[0].suit and s.cards[-1].rank + 1 == cards[0].rank:
  21437.                 return 1
  21438.             
  21439.             if s.cards[-1].rank == QUEEN and cards[0].rank == ACE:
  21440.                 return 1
  21441.             
  21442.         
  21443.         s = _.game.s.rows[(_.id + 1) % 54]
  21444.         if s.cards:
  21445.             if s.cards[-1].suit == cards[0].suit and s.cards[-1].rank - 1 == cards[0].rank:
  21446.                 return 1
  21447.             
  21448.         
  21449.         return 0
  21450.  
  21451.     prepareBottom = Stack.prepareInvisibleBottom
  21452.     
  21453.     def getBottomImage(_):
  21454.         return _.game.app.images.getReserveBottom()
  21455.  
  21456.  
  21457.  
  21458. class Maze(Game):
  21459.     GAME_VERSION = 2
  21460.     Hint_Class = SiebenBisAs_Hint
  21461.     
  21462.     def createGame(_):
  21463.         (l, s) = (Layout(_, XM = 4, YM = 4), _.s)
  21464.         _.setSize(l.XM + 9 * l.XS, l.YM + 6 * l.YS)
  21465.         for i in range(6):
  21466.             for j in range(9):
  21467.                 (x, y) = (l.XM + j * l.XS, l.YM + i * l.YS)
  21468.                 s.rows.append(Maze_RowStack(x, y, _, max_accept = 1, max_cards = 1))
  21469.             
  21470.         
  21471.         s.talon = InitialDealTalonStack((_.width - l.XS) + 1, _.height - l.YS, _)
  21472.         s.internals.append(InvisibleStack(_))
  21473.         l.defaultStackGroups()
  21474.  
  21475.     
  21476.     def startGame(_):
  21477.         frames = 0
  21478.         for i in range(54):
  21479.             c = _.s.talon.cards[-1]
  21480.             if c.rank == KING:
  21481.                 _.s.talon.dealRow(rows = _.s.internals, frames = 0)
  21482.             elif frames == 0 and i >= 36:
  21483.                 _.startDealSample()
  21484.                 frames = -1
  21485.             
  21486.             _.s.talon.dealRow(rows = (_.s.rows[i],), frames = frames)
  21487.         
  21488.         if not __debug__ and len(_.s.talon.cards) == 0:
  21489.             raise AssertionError
  21490.  
  21491.     
  21492.     def isGameWon(_):
  21493.         rows = filter((lambda s: s.cards), _.s.rows)
  21494.         if len(rows) != 48:
  21495.             return 0
  21496.         
  21497.         i = 0
  21498.         if 1:
  21499.             while rows[i].cards[-1].rank != ACE:
  21500.                 i = i + 1
  21501.             rows = rows + rows
  21502.         
  21503.         for j in (i + 0, i + 12, i + 24, i + 36):
  21504.             r1 = rows[j]
  21505.             r2 = rows[j + 11]
  21506.             if r1.cards[-1].rank != ACE or r2.cards[-1].rank != QUEEN:
  21507.                 return 0
  21508.             
  21509.             pile = getPileFromStacks(rows[j:j + 12])
  21510.             if not pile or not isSameSuitSequence(pile, dir = 1):
  21511.                 return 0
  21512.             
  21513.         
  21514.         return 1
  21515.  
  21516.  
  21517. registerGame(GameInfo(118, SiebenBisAs, 'Sieben bis As', GI.GT_MONTANA, 1, 0, ranks = (0, 6, 7, 8, 9, 10, 11, 12)))
  21518. registerGame(GameInfo(144, Maze, 'Maze', GI.GT_MONTANA | GI.GT_OPEN, 1, 0, si = {
  21519.     'ncards': 48 }))
  21520.  
  21521. class DieBoeseSieben_Talon(DieKoenigsbergerin_Talon):
  21522.     
  21523.     def canDealCards(_):
  21524.         if not len(_.cards):
  21525.             pass
  21526.         return _.round != _.max_rounds
  21527.  
  21528.     
  21529.     def dealCards(_, sound = 0):
  21530.         if _.cards:
  21531.             return DieKoenigsbergerin_Talon.dealCards(_, sound = sound)
  21532.         
  21533.         (game, num_cards) = (_.game, len(_.cards))
  21534.         for r in game.s.rows:
  21535.             while r.cards:
  21536.                 num_cards = num_cards + 1
  21537.                 game.moveMove(1, r, _, frames = 0)
  21538.                 continue
  21539.                 None if r.cards[-1].face_up else game.s.rows
  21540.         
  21541.         if not __debug__ and len(_.cards) == num_cards:
  21542.             raise AssertionError
  21543.         if sound:
  21544.             game.startDealSample()
  21545.         
  21546.         game.shuffleStackMove(_)
  21547.         game.nextRoundMove(_)
  21548.         n = len(game.s.rows)
  21549.         flip = num_cards / n & 1
  21550.         while _.cards:
  21551.             if len(_.cards) <= n:
  21552.                 flip = 1
  21553.             
  21554.             _.dealRow(flip = flip)
  21555.             flip = not flip
  21556.         if sound:
  21557.             game.stopSamples()
  21558.         
  21559.         return num_cards
  21560.  
  21561.  
  21562.  
  21563. class DieBoeseSieben(Game):
  21564.     
  21565.     def createGame(_, rows = 7):
  21566.         (l, s) = (Layout(_), _.s)
  21567.         _.setSize(l.XM + max(8, rows) * l.XS, l.YM + 5 * l.YS)
  21568.         for i in range(8):
  21569.             (x, y) = (l.XM + i * l.XS, l.YM)
  21570.             s.foundations.append(DieRussische_Foundation(x, y, _, i / 2, max_move = 0))
  21571.         
  21572.         for i in range(rows):
  21573.             (x, y) = (l.XM + (2 * i + 8 - rows) * l.XS / 2, l.YM + l.YS)
  21574.             s.rows.append(AC_RowStack(x, y, _))
  21575.         
  21576.         s.talon = DieBoeseSieben_Talon(l.XM, _.height - l.YS, _, max_rounds = 2)
  21577.         l.createText(s.talon, 'se')
  21578.         l.defaultStackGroups()
  21579.  
  21580.     
  21581.     def startGame(_):
  21582.         _.startDealSample()
  21583.         for flip in (1, 0, 1, 0, 1, 0, 1):
  21584.             _.s.talon.dealRow(flip = flip)
  21585.         
  21586.  
  21587.  
  21588. registerGame(GameInfo(120, DieBoeseSieben, 'Die b\xf6se Sieben', GI.GT_2DECK_TYPE, 2, 1, ranks = (0, 6, 7, 8, 9, 10, 11, 12)))
  21589.  
  21590. class Terrace_Talon(WasteTalonStack):
  21591.     
  21592.     def canDealCards(_):
  21593.         if _.game.getState() == 0:
  21594.             return 0
  21595.         
  21596.         return WasteTalonStack.canDealCards(_)
  21597.  
  21598.  
  21599.  
  21600. class Terrace_AC_Foundation(AC_FoundationStack):
  21601.     
  21602.     def __init__(_, x, y, game, suit, **cap):
  21603.         kwdefault(cap, mod = 13, min_cards = 1, max_move = 0)
  21604.         apply(AC_FoundationStack.__init__, (_, x, y, game, suit), cap)
  21605.  
  21606.     
  21607.     def acceptsCards(_, from_stack, cards):
  21608.         if _.game.getState() == 0:
  21609.             if len(cards) != 1 or not (cards[0].face_up):
  21610.                 return 0
  21611.             
  21612.             if cards[0].suit != _.cap.base_suit:
  21613.                 return 0
  21614.             
  21615.             return from_stack in _.game.s.rows
  21616.         
  21617.         return AC_FoundationStack.acceptsCards(_, from_stack, cards)
  21618.  
  21619.  
  21620.  
  21621. class Terrace_SS_Foundation(SS_FoundationStack):
  21622.     
  21623.     def __init__(_, x, y, game, suit, **cap):
  21624.         kwdefault(cap, mod = 13, min_cards = 1, max_move = 0)
  21625.         apply(SS_FoundationStack.__init__, (_, x, y, game, suit), cap)
  21626.  
  21627.     
  21628.     def acceptsCards(_, from_stack, cards):
  21629.         if _.game.getState() == 0:
  21630.             if len(cards) != 1 or not (cards[0].face_up):
  21631.                 return 0
  21632.             
  21633.             if cards[0].suit != _.cap.base_suit:
  21634.                 return 0
  21635.             
  21636.             return from_stack in _.game.s.rows
  21637.         
  21638.         return SS_FoundationStack.acceptsCards(_, from_stack, cards)
  21639.  
  21640.  
  21641.  
  21642. class Terrace_RowStack(AC_RowStack):
  21643.     
  21644.     def __init__(_, x, y, game, **cap):
  21645.         kwdefault(cap, mod = 13, max_move = 1)
  21646.         apply(AC_RowStack.__init__, (_, x, y, game), cap)
  21647.  
  21648.     
  21649.     def acceptsCards(_, from_stack, cards):
  21650.         if _.game.getState() == 0:
  21651.             return 0
  21652.         
  21653.         if from_stack in _.game.s.reserves:
  21654.             return 0
  21655.         
  21656.         return AC_RowStack.acceptsCards(_, from_stack, cards)
  21657.  
  21658.     
  21659.     def moveMove(_, ncards, to_stack, frames = -1, shadow = -1):
  21660.         state = _.game.getState()
  21661.         if state > 0:
  21662.             AC_RowStack.moveMove(_, ncards, to_stack, frames = frames, shadow = shadow)
  21663.             return None
  21664.         
  21665.         if not __debug__ and to_stack in _.game.s.foundations:
  21666.             raise AssertionError
  21667.         if not __debug__ and ncards == 1:
  21668.             raise AssertionError
  21669.         if not __debug__ and not (_.game.s.waste.cards):
  21670.             raise AssertionError
  21671.         _.game.moveMove(ncards, _, to_stack, frames = frames, shadow = shadow)
  21672.         for s in _.game.s.foundations:
  21673.             s.cap.base_rank = to_stack.cards[0].rank
  21674.         
  21675.         freerows = filter((lambda s: not (s.cards)), _.game.s.rows)
  21676.         _.game.s.talon.dealRow(rows = freerows, sound = 1)
  21677.         _.game.s.talon.dealCards()
  21678.  
  21679.     
  21680.     def getBottomImage(_):
  21681.         return _.game.app.images.getReserveBottom()
  21682.  
  21683.  
  21684.  
  21685. class Terrace(Game):
  21686.     Foundation_Class = Terrace_AC_Foundation
  21687.     RowStack_Class = Terrace_RowStack
  21688.     ReserveStack_Class = OpenStack
  21689.     Hint_Class = CautiousDefaultHint
  21690.     INITIAL_RESERVE_CARDS = 11
  21691.     
  21692.     def createGame(_, rows = 9, max_rounds = 1, num_deal = 1):
  21693.         (l, s) = (Layout(_, XOFFSET = 14), _.s)
  21694.         maxrows = max(rows, 9)
  21695.         (w1, w2) = ((maxrows - 8) * l.XS / 2, (maxrows - rows) * l.XS / 2)
  21696.         h = max(3 * l.YS, 20 * l.YOFFSET)
  21697.         _.setSize(l.XM + maxrows * l.XS + l.XM, l.YM + 2 * l.YS + h)
  21698.         _.base_card = None
  21699.         (x, y) = (l.XM + w1, l.YM)
  21700.         s.talon = Terrace_Talon(x, y, _, max_rounds = max_rounds, num_deal = num_deal)
  21701.         l.createText(s.talon, 'sw')
  21702.         x = x + l.XS
  21703.         s.waste = WasteStack(x, y, _)
  21704.         l.createText(s.waste, 'se', text_format = '%D')
  21705.         x = x + 2 * l.XS
  21706.         stack = _.ReserveStack_Class(x, y, _)
  21707.         stack.CARD_XOFFSET = l.XOFFSET
  21708.         l.createText(stack, 'sw')
  21709.         s.reserves.append(stack)
  21710.         (x, y) = (l.XM + w1, y + l.YS)
  21711.         for i in range(8):
  21712.             s.foundations.append(_.Foundation_Class(x, y, _, suit = i / 2))
  21713.             x = x + l.XS
  21714.         
  21715.         (x, y) = (l.XM + w2, y + l.YS)
  21716.         for i in range(rows):
  21717.             s.rows.append(_.RowStack_Class(x, y, _))
  21718.             x = x + l.XS
  21719.         
  21720.         l.defaultStackGroups()
  21721.  
  21722.     
  21723.     def getState(_):
  21724.         for s in _.s.foundations:
  21725.             pass
  21726.         
  21727.         return 0
  21728.  
  21729.     
  21730.     def startGame(_):
  21731.         _.startDealSample()
  21732.         for i in range(_.INITIAL_RESERVE_CARDS):
  21733.             _.s.talon.dealRow(rows = _.s.reserves)
  21734.         
  21735.         _.s.talon.dealRow(rows = _.s.rows[:4])
  21736.  
  21737.     
  21738.     def fillStack(_, stack):
  21739.         if not (stack.cards):
  21740.             old_state = _.enterState(_.S_FILL)
  21741.             if stack is _.s.waste and _.s.talon.cards:
  21742.                 _.s.talon.dealCards()
  21743.             elif stack in _.s.rows and _.s.waste.cards:
  21744.                 _.s.waste.moveMove(1, stack)
  21745.             
  21746.             _.leaveState(old_state)
  21747.         
  21748.  
  21749.     
  21750.     def _restoreGameHook(_, game):
  21751.         for s in _.s.foundations:
  21752.             s.cap.base_rank = game.loadinfo.base_rank
  21753.         
  21754.  
  21755.     
  21756.     def _loadGameHook(_, p):
  21757.         _.loadinfo.addattr(base_rank = p.load())
  21758.  
  21759.     
  21760.     def _saveGameHook(_, p):
  21761.         base_rank = NO_RANK
  21762.         for s in _.s.foundations:
  21763.             pass
  21764.         
  21765.         p.dump(base_rank)
  21766.  
  21767.  
  21768.  
  21769. class GeneralsPatience(Terrace):
  21770.     Foundation_Class = Terrace_SS_Foundation
  21771.     INITIAL_RESERVE_CARDS = 13
  21772.  
  21773.  
  21774. class BlondesAndBrunettes(Terrace):
  21775.     INITIAL_RESERVE_CARDS = 10
  21776.     
  21777.     def startGame(_):
  21778.         _.startDealSample()
  21779.         for i in range(_.INITIAL_RESERVE_CARDS):
  21780.             _.s.talon.dealRow(rows = _.s.reserves)
  21781.         
  21782.         _.s.talon.dealRow()
  21783.         c = _.s.talon.getCard()
  21784.         for s in _.s.foundations:
  21785.             s.cap.base_rank = c.rank
  21786.         
  21787.         _.s.talon.dealRow(rows = (_.s.foundations[2 * c.suit],))
  21788.         _.s.talon.dealCards()
  21789.  
  21790.     
  21791.     def getState(_):
  21792.         return 1
  21793.  
  21794.  
  21795.  
  21796. class FallingStar(BlondesAndBrunettes):
  21797.     INITIAL_RESERVE_CARDS = 11
  21798.  
  21799. registerGame(GameInfo(135, Terrace, 'Terrace', GI.GT_TERRACE, 2, 0, altnames = 'Queen of Italy'))
  21800. registerGame(GameInfo(136, GeneralsPatience, "General's Patience", GI.GT_TERRACE, 2, 0))
  21801. registerGame(GameInfo(137, BlondesAndBrunettes, 'Blondes and Brunettes', GI.GT_TERRACE, 2, 0))
  21802. registerGame(GameInfo(138, FallingStar, 'Falling Star', GI.GT_TERRACE, 2, 0))
  21803.  
  21804. class PokerSquare_RowStack(ReserveStack):
  21805.     
  21806.     def clickHandler(_, event):
  21807.         if not (_.cards):
  21808.             _.game.s.talon.playMoveMove(1, _)
  21809.             return 1
  21810.         
  21811.         return ReserveStack.clickHandler(_, event)
  21812.  
  21813.     rightclickHandler = clickHandler
  21814.  
  21815.  
  21816. class PokerSquare(Game):
  21817.     Talon_Class = OpenTalonStack
  21818.     RowStack_Class = StackWrapper(PokerSquare_RowStack, max_move = 0)
  21819.     Hint_Class = None
  21820.     WIN_SCORE = 100
  21821.     
  21822.     def createGame(_):
  21823.         (l, s) = (Layout(_), _.s)
  21824.         ta = 'ss'
  21825.         (x, y) = (l.XM, l.YM + 2 * l.YS)
  21826.         if _.preview <= 1:
  21827.             t = MfxCanvasText(_.canvas, x, y, anchor = 'nw', text = 'Royal Flush\nStraight Flush\nFour of a Kind\n' + 'Full House\nFlush\nStraight\n' + 'Three of a Kind\nTwo Pair\nOne Pair')
  21828.             bb = t.bbox()
  21829.             x = bb[2] + 16
  21830.             h = bb[3] - bb[1]
  21831.             if h >= 2 * l.YS:
  21832.                 ta = 'e'
  21833.                 t.move(0, -(l.YS))
  21834.                 y = y - l.YS
  21835.             
  21836.             t = MfxCanvasText(_.canvas, x, y, anchor = 'nw', text = '100\n75\n50\n25\n20\n15\n10\n5\n2')
  21837.             x = t.bbox()[2] + 16
  21838.             _.texts.misc = MfxCanvasText(_.canvas, x, y, anchor = 'nw', text = '0\n' * 8 + '0')
  21839.             x = _.texts.misc.bbox()[2] + 32
  21840.         
  21841.         w = max(2 * l.XS, x)
  21842.         _.setSize(l.XM + w + 5 * l.XS + 50, l.YM + 5 * l.YS + 30)
  21843.         for i in range(5):
  21844.             for j in range(5):
  21845.                 (x, y) = (l.XM + w + j * l.XS, l.YM + i * l.YS)
  21846.                 s.rows.append(_.RowStack_Class(x, y, _))
  21847.             
  21848.         
  21849.         (x, y) = (l.XM, l.YM)
  21850.         s.talon = _.Talon_Class(x, y, _)
  21851.         l.createText(s.talon, anchor = ta)
  21852.         s.internals.append(InvisibleStack(_))
  21853.         r = s.rows
  21854.         _.poker_hands = [
  21855.             r[0:5],
  21856.             r[5:10],
  21857.             r[10:15],
  21858.             r[15:20],
  21859.             r[20:25],
  21860.             (r[0], r[0 + 5], r[0 + 10], r[0 + 15], r[0 + 20]),
  21861.             (r[1], r[1 + 5], r[1 + 10], r[1 + 15], r[1 + 20]),
  21862.             (r[2], r[2 + 5], r[2 + 10], r[2 + 15], r[2 + 20]),
  21863.             (r[3], r[3 + 5], r[3 + 10], r[3 + 15], r[3 + 20]),
  21864.             (r[4], r[4 + 5], r[4 + 10], r[4 + 15], r[4 + 20])]
  21865.         _.poker_hands = map(tuple, _.poker_hands)
  21866.         l.defaultStackGroups()
  21867.         return l
  21868.  
  21869.     
  21870.     def startGame(_):
  21871.         _.moveMove(27, _.s.talon, _.s.internals[0], frames = 0)
  21872.         _.s.talon.fillStack()
  21873.  
  21874.     
  21875.     def isGameWon(_):
  21876.         if len(_.s.talon.cards) == 0:
  21877.             pass
  21878.         return _.getGameScore() >= _.WIN_SCORE
  21879.  
  21880.     
  21881.     def getAutoStacks(_, event = None):
  21882.         return ((), (), ())
  21883.  
  21884.     
  21885.     def updateText(_):
  21886.         if _.preview > 1:
  21887.             return None
  21888.         
  21889.         score = 0
  21890.         count = [
  21891.             0] * 9
  21892.         for i in range(10):
  21893.             (type, value) = _.getHandScore(_.poker_hands[i])
  21894.             if type <= type:
  21895.                 pass
  21896.             elif type <= 8:
  21897.                 count[type] = count[type] + 1
  21898.             
  21899.             _.texts.hands[i].config(text = str(value))
  21900.             score = score + value
  21901.         
  21902.         t = string.join(map(str, count), '\n')
  21903.         _.texts.misc.config(text = t)
  21904.         t = ''
  21905.         if score >= _.WIN_SCORE:
  21906.             t = 'WON\n\n'
  21907.         
  21908.         if _.s.talon.cards:
  21909.             t = t + 'Points: %d' % score
  21910.         else:
  21911.             t = t + 'Total: %d' % score
  21912.         _.texts.score.config(text = t)
  21913.  
  21914.     
  21915.     def getGameScore(_):
  21916.         score = 0
  21917.         for hand in _.poker_hands:
  21918.             (type, value) = _.getHandScore(hand)
  21919.             score = score + value
  21920.         
  21921.         return score
  21922.  
  21923.     
  21924.     def getHandScore(_, hand):
  21925.         same_rank = [
  21926.             0] * 13
  21927.         same_suit = [
  21928.             0] * 4
  21929.         ranks = []
  21930.         for s in hand:
  21931.             pass
  21932.         
  21933.         straight = 0
  21934.         if same_rank.count(1) == 5:
  21935.             d = max(ranks) - min(ranks)
  21936.             if d == 4:
  21937.                 straight = 1
  21938.             elif d == 12 and same_rank[-4:].count(1) == 4:
  21939.                 straight = 2
  21940.             
  21941.         
  21942.         if max(same_suit) == 5:
  21943.             if straight:
  21944.                 if straight == 2:
  21945.                     return (0, 100)
  21946.                 
  21947.                 return (1, 75)
  21948.             
  21949.             return (4, 20)
  21950.         
  21951.         if straight:
  21952.             return (5, 15)
  21953.         
  21954.         if max(same_rank) >= 2:
  21955.             same_rank.sort()
  21956.             if same_rank[-1] == 4:
  21957.                 return (2, 50)
  21958.             
  21959.             if same_rank[-1] == 3:
  21960.                 if same_rank[-2] == 2:
  21961.                     return (3, 25)
  21962.                 
  21963.                 return (6, 10)
  21964.             
  21965.             if same_rank[-2] == 2:
  21966.                 return (7, 5)
  21967.             
  21968.             return (8, 2)
  21969.         
  21970.         return (-1, 0)
  21971.  
  21972.  
  21973.  
  21974. class PokerShuffle_RowStack(ReserveStack):
  21975.     
  21976.     def moveMove(_, ncards, to_stack, frames = -1, shadow = -1):
  21977.         if __debug__:
  21978.             if not ncards == 1 and to_stack in _.game.s.rows:
  21979.                 raise AssertionError
  21980.         if not __debug__ and len(to_stack.cards) == 1:
  21981.             raise AssertionError
  21982.         _._swapPairMove(ncards, to_stack, frames = -1, shadow = 0)
  21983.  
  21984.     
  21985.     def _swapPairMove(_, n, other_stack, frames = -1, shadow = -1):
  21986.         game = _.game
  21987.         old_state = game.enterState(game.S_FILL)
  21988.         swap = game.s.internals[0]
  21989.         game.moveMove(n, _, swap, frames = 0)
  21990.         game.moveMove(n, other_stack, _, frames = frames, shadow = shadow)
  21991.         game.moveMove(n, swap, other_stack, frames = 0)
  21992.         game.leaveState(old_state)
  21993.  
  21994.  
  21995.  
  21996. class PokerShuffle(PokerSquare):
  21997.     Talon_Class = InitialDealTalonStack
  21998.     RowStack_Class = StackWrapper(PokerShuffle_RowStack, max_accept = 1, max_cards = 2)
  21999.     WIN_SCORE = 200
  22000.     
  22001.     def createGame(_):
  22002.         l = PokerSquare.createGame(_)
  22003.         if _.s.talon.texts.ncards:
  22004.             _.s.talon.texts.ncards.text_format = '%D'
  22005.         
  22006.  
  22007.     
  22008.     def startGame(_):
  22009.         _.moveMove(27, _.s.talon, _.s.internals[0], frames = 0)
  22010.         _.startDealSample()
  22011.         _.s.talon.dealRow()
  22012.  
  22013.     
  22014.     def checkForWin(_):
  22015.         return 0
  22016.  
  22017.  
  22018. registerGame(GameInfo(139, PokerSquare, 'Poker Square', GI.GT_POKER_TYPE | GI.GT_SCORE, 1, 0, si = {
  22019.     'ncards': 25 }))
  22020. registerGame(GameInfo(140, PokerShuffle, 'Poker Shuffle', GI.GT_POKER_TYPE | GI.GT_SCORE | GI.GT_OPEN, 1, 0, si = {
  22021.     'ncards': 25 }))
  22022.  
  22023. class DerKatzenschwanz(Game):
  22024.     RowStack_Class = StackWrapper(AC_RowStack, base_rank = NO_RANK)
  22025.     
  22026.     def createGame(_, rows = 9, reserves = 8):
  22027.         (l, s) = (Layout(_, XOFFSET = 10), _.s)
  22028.         maxrows = max(rows, reserves)
  22029.         _.setSize(l.XM + (maxrows + 2) * l.XS, l.YM + 6 * l.YS)
  22030.         playcards = 4 * l.YS / l.YOFFSET
  22031.         (xoffset, yoffset) = ([], [])
  22032.         for i in range(playcards):
  22033.             xoffset.append(0)
  22034.             yoffset.append(l.YOFFSET)
  22035.         
  22036.         for i in range(104 - playcards):
  22037.             xoffset.append(l.XOFFSET)
  22038.             yoffset.append(0)
  22039.         
  22040.         (x, y) = (l.XM + (maxrows - reserves) * l.XS / 2, l.YM)
  22041.         for i in range(reserves):
  22042.             s.reserves.append(ReserveStack(x, y, _))
  22043.             x = x + l.XS
  22044.         
  22045.         (x, y) = (l.XM + (maxrows - rows) * l.XS / 2, l.YM + l.YS)
  22046.         _.setRegion(s.reserves, (-999, -999, 999999, y - l.XM / 2))
  22047.         for i in range(rows):
  22048.             stack = _.RowStack_Class(x, y, _)
  22049.             stack.CARD_XOFFSET = xoffset
  22050.             stack.CARD_YOFFSET = yoffset
  22051.             s.rows.append(stack)
  22052.             x = x + l.XS
  22053.         
  22054.         (x, y) = (l.XM + maxrows * l.XS, l.YM)
  22055.         for suit in range(4):
  22056.             for i in range(2):
  22057.                 s.foundations.append(SS_FoundationStack(x + i * l.XS, y, _, suit = suit))
  22058.             
  22059.             y = y + l.YS
  22060.         
  22061.         _.setRegion(_.s.foundations, (x - l.CW / 2, -999, 999999, y), priority = 1)
  22062.         s.talon = InitialDealTalonStack(_.width - 3 * l.XS / 2, _.height - l.YS, _)
  22063.         l.defaultStackGroups()
  22064.  
  22065.     
  22066.     def startGame(_):
  22067.         _.startDealSample()
  22068.         i = 0
  22069.         while _.s.talon.cards:
  22070.             if _.s.talon.cards[-1].rank == KING:
  22071.                 if _.s.rows[i].cards:
  22072.                     i = i + 1
  22073.                 
  22074.             
  22075.             _.s.talon.dealRow(rows = [
  22076.                 _.s.rows[i]], frames = 4)
  22077.  
  22078.     
  22079.     def shallHighlightMatch(_, stack1, card1, stack2, card2):
  22080.         if not card1.color != card2.color and card1.rank + 1 == card2.rank:
  22081.             pass
  22082.         return card2.rank + 1 == card1.rank
  22083.  
  22084.     
  22085.     def _getClosestStack(_, cx, cy, stacks, dragstack):
  22086.         (closest, cdist) = (None, 999999999)
  22087.         for stack in stacks:
  22088.             if dist < cdist:
  22089.                 (closest, cdist) = (stack, dist)
  22090.             
  22091.         
  22092.         return closest
  22093.  
  22094.  
  22095.  
  22096. class DieSchlange(DerKatzenschwanz):
  22097.     RowStack_Class = StackWrapper(FreeCell_AC_RowStack, base_rank = NO_RANK)
  22098.     Hint_Class = FreeCellType_Hint
  22099.     
  22100.     def createGame(_):
  22101.         DerKatzenschwanz.createGame(_, rows = 9, reserves = 7)
  22102.  
  22103.     
  22104.     def startGame(_):
  22105.         _.startDealSample()
  22106.         i = 0
  22107.         while _.s.talon.cards:
  22108.             c = _.s.talon.cards[-1]
  22109.             if c.rank == ACE:
  22110.                 to_stack = _.s.foundations[c.suit * 2]
  22111.                 if to_stack.cards:
  22112.                     to_stack = _.s.foundations[c.suit * 2 + 1]
  22113.                 
  22114.             elif c.rank == KING and _.s.rows[i].cards:
  22115.                 i = i + 1
  22116.             
  22117.             to_stack = _.s.rows[i]
  22118.             _.s.talon.dealRow(rows = (to_stack,), frames = 4)
  22119.  
  22120.  
  22121. registerGame(GameInfo(141, DerKatzenschwanz, 'Der Katzenschwanz', GI.GT_FREECELL | GI.GT_OPEN, 2, 0))
  22122. registerGame(GameInfo(142, DieSchlange, 'Die Schlange', GI.GT_FREECELL | GI.GT_OPEN, 2, 0))
  22123.  
  22124. class Napoleon_Talon(InitialDealTalonStack):
  22125.     pass
  22126.  
  22127.  
  22128. class Napoleon_Foundation(Braid_Foundation):
  22129.     pass
  22130.  
  22131.  
  22132. class Napoleon_RowStack(BasicRowStack):
  22133.     
  22134.     def __init__(_, x, y, game, **cap):
  22135.         kwdefault(cap, mod = 13, max_move = 1, max_accept = 1)
  22136.         apply(BasicRowStack.__init__, (_, x, y, game), cap)
  22137.  
  22138.     
  22139.     def acceptsCards(_, from_stack, cards):
  22140.         if not OpenStack.acceptsCards(_, from_stack, cards):
  22141.             return 0
  22142.         
  22143.         if not (_.cards):
  22144.             return 1
  22145.         
  22146.         (c1, c2) = (_.cards[-1], cards[0])
  22147.         if c1.suit != c2.suit:
  22148.             return 0
  22149.         
  22150.         if not c1.rank == (c2.rank + 1) % _.cap.mod:
  22151.             pass
  22152.         return c2.rank == (c1.rank + 1) % _.cap.mod
  22153.  
  22154.     
  22155.     def getBottomImage(_):
  22156.         return _.game.app.images.getReserveBottom()
  22157.  
  22158.  
  22159.  
  22160. class Napoleon_ReserveStack(BasicRowStack):
  22161.     
  22162.     def __init__(_, x, y, game, **cap):
  22163.         kwdefault(cap, max_move = 1, max_accept = 0)
  22164.         apply(BasicRowStack.__init__, (_, x, y, game), cap)
  22165.  
  22166.  
  22167.  
  22168. class Napoleon_SingleFreeCell(ReserveStack):
  22169.     
  22170.     def acceptsCards(_, from_stack, cards):
  22171.         return ReserveStack.acceptsCards(_, from_stack, cards)
  22172.  
  22173.     
  22174.     def canMoveCards(_, cards):
  22175.         if _.game.s.rows[8].cards and _.game.s.rows[9].cards:
  22176.             return 0
  22177.         
  22178.         return ReserveStack.canMoveCards(_, cards)
  22179.  
  22180.  
  22181.  
  22182. class Napoleon_FreeCell(ReserveStack):
  22183.     
  22184.     def canMoveCards(_, cards):
  22185.         if _.game.s.rows[_.id - 2].cards:
  22186.             return 0
  22187.         
  22188.         return ReserveStack.canMoveCards(_, cards)
  22189.  
  22190.  
  22191.  
  22192. class DerKleineNapoleon(Game):
  22193.     
  22194.     def createGame(_, reserves = 1):
  22195.         (l, s) = (Layout(_), _.s)
  22196.         _.setSize(l.XM + 2 * 24 + 2 * l.XM + 11 * l.XS, l.YM + 5 * l.YS + 2 * l.XM)
  22197.         x0 = l.XM + 24 + 4 * l.XS
  22198.         x1 = x0 + l.XS + l.XM
  22199.         x2 = x1 + l.XS + l.XM
  22200.         y = l.YM
  22201.         for i in range(4):
  22202.             s.rows.append(Napoleon_RowStack(x0, y, _))
  22203.             s.rows.append(Napoleon_RowStack(x2, y, _))
  22204.             y = y + l.YS
  22205.         
  22206.         y = _.height - l.YS
  22207.         (x, y) = (x1, l.YM)
  22208.         for i in range(4):
  22209.             s.foundations.append(Napoleon_Foundation(x, y, _, i))
  22210.             y = y + l.YS
  22211.         
  22212.         s.talon = Napoleon_Talon(x, y, _)
  22213.         for r in s.rows:
  22214.             r.CARD_YOFFSET = 0
  22215.         
  22216.         l.defaultStackGroups()
  22217.  
  22218.     
  22219.     def _shuffleHook(_, cards):
  22220.         rank = cards[-1].rank
  22221.         return _._shuffleHookMoveToBottom(cards, (lambda c, rank = rank: (c.rank == rank, c.suit)))
  22222.  
  22223.     
  22224.     def startGame(_):
  22225.         for i in range(4):
  22226.             _.s.talon.dealRow(rows = _.s.rows[:8], frames = 0)
  22227.         
  22228.         _.startDealSample()
  22229.         _.s.talon.dealRow(rows = _.s.rows[:8])
  22230.         for i in range(4):
  22231.             _.s.talon.dealRow(rows = _.s.rows[8:])
  22232.         
  22233.         _.s.talon.dealBaseCards(ncards = 4)
  22234.  
  22235.     
  22236.     def shallHighlightMatch(_, stack1, card1, stack2, card2):
  22237.         if not card1.suit == card2.suit and (card1.rank + 1) % 13 == card2.rank:
  22238.             pass
  22239.         return (card2.rank + 1) % 13 == card1.rank
  22240.  
  22241.     
  22242.     def updateText(_):
  22243.         if _.preview > 1 or not (_.texts.info):
  22244.             return None
  22245.         
  22246.         t = ''
  22247.         f = _.s.foundations[0]
  22248.         if f.cards:
  22249.             t = RANKS[f.cards[0].rank]
  22250.             dir = _.getFoundationDir()
  22251.             if dir == 1:
  22252.                 t = t + ' Ascending'
  22253.             elif dir == -1:
  22254.                 t = t + ' Descending'
  22255.             
  22256.         
  22257.         _.texts.info.config(text = t)
  22258.  
  22259.  
  22260.  
  22261. class DerFreieNapoleon(DerKleineNapoleon):
  22262.     
  22263.     def createGame(_, reserves = 1):
  22264.         (l, s) = (Layout(_), _.s)
  22265.         h = l.CH * 2 / 3 + (15 - 1) * l.YOFFSET
  22266.         h = l.YS + max(h, 3 * l.YS)
  22267.         _.setSize(l.XM + 2 * l.XM + 10 * l.XS, l.YM + h)
  22268.         x1 = l.XM + 8 * l.XS + 2 * l.XM
  22269.         y = l.YM + l.YS
  22270.         for j in range(8):
  22271.             x = l.XM + j * l.XS
  22272.             s.rows.append(Napoleon_RowStack(x, y, _))
  22273.         
  22274.         for j in range(2):
  22275.             x = x1 + j * l.XS
  22276.             s.rows.append(Napoleon_ReserveStack(x, y, _))
  22277.         
  22278.         _.setRegion(s.rows, (-999, y - l.YM / 2, 999999, 999999))
  22279.         y = l.YM
  22280.         x = l.XM + 2 * l.XS
  22281.         for i in range(4):
  22282.             s.foundations.append(Napoleon_Foundation(x, y, _, i))
  22283.             x = x + l.XS
  22284.         
  22285.         (tx, ty, ta, tf) = l.getTextAttr(s.foundations[-1], 'se')
  22286.         font = getFont('canvas_card', cardw = l.CW)
  22287.         _.texts.info = MfxCanvasText(_.canvas, tx + l.XM, ty, anchor = ta, font = font)
  22288.         (x, y) = (l.XM, _.height - l.YS)
  22289.         s.talon = Napoleon_Talon(x, y, _)
  22290.         l.defaultStackGroups()
  22291.  
  22292.  
  22293.  
  22294. class Napoleon(DerKleineNapoleon):
  22295.     
  22296.     def createGame(_):
  22297.         DerKleineNapoleon.createGame(_, reserves = 2)
  22298.  
  22299.  
  22300.  
  22301. class FreeNapoleon(DerFreieNapoleon):
  22302.     
  22303.     def createGame(_):
  22304.         DerFreieNapoleon.createGame(_, reserves = 2)
  22305.  
  22306.  
  22307. registerGame(GameInfo(167, DerKleineNapoleon, 'Der kleine Napoleon', GI.GT_NAPOLEON | GI.GT_OPEN, 1, 0))
  22308. registerGame(GameInfo(168, DerFreieNapoleon, 'Der freie Napoleon', GI.GT_NAPOLEON | GI.GT_OPEN, 1, 0))
  22309. registerGame(GameInfo(169, Napoleon, 'Napoleon', GI.GT_NAPOLEON | GI.GT_OPEN, 1, 0))
  22310. registerGame(GameInfo(170, FreeNapoleon, 'Free Napoleon', GI.GT_NAPOLEON | GI.GT_OPEN, 1, 0))
  22311.  
  22312. class LarasGame_Hint(CautiousDefaultHint):
  22313.     pass
  22314.  
  22315.  
  22316. class LarasGame_Talon(WasteTalonStack):
  22317.     
  22318.     def dealRow(_, rows = None, flip = 1, reverse = 0, frames = -1):
  22319.         if rows is None:
  22320.             rows = _.game.s.rows
  22321.         
  22322.         old_state = _.game.enterState(_.game.S_DEAL)
  22323.         temp = _.dealToStacks(rows, flip, reverse, frames)
  22324.         if len(_.cards) > 2:
  22325.             _.game.moveMove(1, _, _.game.s.waste, frames = frames)
  22326.             _.game.moveMove(1, _, _.game.s.waste, frames = frames)
  22327.         
  22328.         _.game.leaveState(old_state)
  22329.         return temp + 2
  22330.  
  22331.     
  22332.     def dealToStacks(_, stacks, flip = 1, reverse = 0, frames = -1):
  22333.         if len(_.cards) == 0:
  22334.             return 0
  22335.         
  22336.         for r in stacks:
  22337.             if not __debug__ and not (_.getCard().face_up):
  22338.                 raise AssertionError
  22339.             None if len(_.cards) == 0 else stacks
  22340.             if not __debug__ and r is not _:
  22341.                 raise AssertionError
  22342.             if flip:
  22343.                 _.game.flipMove(_)
  22344.             
  22345.             _.game.moveMove(1, _, r, frames = frames)
  22346.             if r.getCard().rank == r.id:
  22347.                 if len(_.cards) == 0:
  22348.                     return 0
  22349.                 
  22350.                 _.game.moveMove(1, _, _.game.s.waste, frames = frames)
  22351.             
  22352.             if r.getCard().rank == ACE:
  22353.                 if len(_.cards) < 2:
  22354.                     return 0
  22355.                 
  22356.                 _.game.moveMove(1, _, _.game.s.waste, frames = frames)
  22357.                 _.game.moveMove(1, _, _.game.s.waste, frames = frames)
  22358.             
  22359.             if r.getCard().rank == JACK and r.getCard().rank == QUEEN or r.getCard().rank == KING:
  22360.                 if len(_.cards) == 0:
  22361.                     return 0
  22362.                 
  22363.                 _.game.moveMove(1, _, _.game.s.waste, frames = frames)
  22364.             
  22365.         
  22366.         return len(stacks)
  22367.  
  22368.     
  22369.     def dealCards(_, sound = 0):
  22370.         num_cards = 0
  22371.         waste = _.game.s.waste
  22372.         if _.cards:
  22373.             curr_rank = _.getCard().rank
  22374.             for i in range(_.game.NUMRESERVES):
  22375.                 pass
  22376.             
  22377.             _.game.flipMove(_)
  22378.             _.game.moveMove(1, _, _.game.s.reserves[0], frames = 4, shadow = 0)
  22379.             res_begin = len(_.game.s.rows[curr_rank].cards) + 1
  22380.             for i in range(len(_.game.s.rows[curr_rank].cards)):
  22381.                 _.game.moveMove(1, _.game.s.rows[curr_rank], _.game.s.reserves[res_begin - (i + 1)], frames = 4, shadow = 0)
  22382.             
  22383.             _.game.old_rank = curr_rank
  22384.         elif waste.cards and _.round != _.max_rounds:
  22385.             num_cards = len(waste.cards)
  22386.             _.game.turnStackMove(waste, _, update_flags = 1)
  22387.         
  22388.         return num_cards
  22389.  
  22390.  
  22391.  
  22392. class LarasGame_RowStack(OpenStack):
  22393.     
  22394.     def __init__(_, x, y, game, **cap):
  22395.         apply(OpenStack.__init__, (_, x, y, game), cap)
  22396.         _.CARD_YOFFSET = 1
  22397.  
  22398.  
  22399.  
  22400. class LarasGame_ReserveStack(OpenStack):
  22401.     pass
  22402.  
  22403.  
  22404. class LarasGame(Game):
  22405.     Hint_Class = LarasGame_Hint
  22406.     NUMRESERVES = 20
  22407.     
  22408.     def createGame(_):
  22409.         (l, s) = (Layout(_, XOFFSET = 10), _.s)
  22410.         _.setSize(l.XM + _.NUMRESERVES * l.XS / 2, l.YM + 6 * l.YS)
  22411.         _.old_rank = 0
  22412.         x = l.XM + l.XS
  22413.         y = l.YM + l.YS
  22414.         for i in range(13):
  22415.             s.rows.append(LarasGame_RowStack(x, y, _, max_move = 1))
  22416.             x = x + l.XS
  22417.         
  22418.         x = l.XM
  22419.         y = l.YM
  22420.         for i in range(4):
  22421.             s.foundations.append(SS_FoundationStack(x, y, _, i, dir = -1, base_rank = KING))
  22422.             y = y + l.YS
  22423.         
  22424.         x = l.XM + l.XS + l.XS
  22425.         y = l.YM
  22426.         for i in range(4):
  22427.             s.foundations.append(SS_FoundationStack(x, y, _, i))
  22428.             x = x + l.XS
  22429.         
  22430.         for i in range(2):
  22431.             (x, y) = (l.XM, l.YM + (5 - i) * l.YS)
  22432.             for i in range(_.NUMRESERVES / 2):
  22433.                 s.reserves.append(LarasGame_ReserveStack(x, y, _))
  22434.                 x = x + l.XS
  22435.             
  22436.         
  22437.         x = l.XM + 5 * l.XS
  22438.         y = l.YM + 3 * l.YS
  22439.         s.talon = LarasGame_Talon(x, y, _, max_rounds = 1)
  22440.         l.createText(s.talon, 'se')
  22441.         x = x - l.XS
  22442.         s.waste = WasteStack(x, y, _)
  22443.         _.sg.openstacks = s.foundations + s.rows
  22444.         _.sg.talonstacks = [
  22445.             s.talon] + [
  22446.             s.waste]
  22447.         _.sg.dropstacks = s.rows + s.reserves + [
  22448.             s.waste]
  22449.  
  22450.     
  22451.     def startGame(_):
  22452.         frames = 0
  22453.         for i in range(8):
  22454.             if i == 4:
  22455.                 _.startDealSample()
  22456.                 frames = 4
  22457.             
  22458.             _.s.talon.dealRow(frames = frames)
  22459.         
  22460.         _.moveMove(len(_.s.waste.cards), _.s.waste, _.s.talon, frames = 0)
  22461.  
  22462.     
  22463.     def shallHighlightMatch(_, stack1, card1, stack2, card2):
  22464.         if not card1.suit == card2.suit and card1.rank + 1 == card2.rank:
  22465.             pass
  22466.         return card2.rank + 1 == card1.rank
  22467.  
  22468.     
  22469.     def getHighlightPilesStacks(_):
  22470.         return ()
  22471.  
  22472.     
  22473.     def _autoDeal(_, sound = 1):
  22474.         return 0
  22475.  
  22476.     
  22477.     def canUndo(_):
  22478.         return 0
  22479.  
  22480.     
  22481.     def canSaveGame(_):
  22482.         return 0
  22483.  
  22484.     
  22485.     def saveGame(_, filename, binmode = 1):
  22486.         _.notYetImplemented()
  22487.  
  22488.     
  22489.     def _restoreGameHook(_, game):
  22490.         _.old_rank = game.loadinfo.old_rank
  22491.  
  22492.     
  22493.     def _loadGameHook(_, p):
  22494.         _.loadinfo.addattr(old_rank = 0)
  22495.         _.loadinfo.old_rank = p.load()
  22496.  
  22497.     
  22498.     def _saveGameHook(_, p):
  22499.         p.dump(_.old_rank)
  22500.  
  22501.  
  22502. registerGame(GameInfo(37, LarasGame, "Lara's Game", GI.GT_2DECK_TYPE | GI.GT_CONTRIB | GI.GT_ORIGINAL, 2, 0))
  22503.  
  22504. class Sanibel(Gypsy):
  22505.     Layout_Method = Layout.klondikeLayout
  22506.     Talon_Class = StackWrapper(WasteTalonStack, max_rounds = 1)
  22507.     Foundation_Class = StackWrapper(SS_FoundationStack, max_move = 0)
  22508.     RowStack_Class = Yukon_AC_RowStack
  22509.     Hint_Class = Yukon_Hint
  22510.     
  22511.     def createGame(_):
  22512.         Gypsy.createGame(_, rows = 10, waste = 1, playcards = 23)
  22513.  
  22514.     
  22515.     def startGame(_):
  22516.         for i in range(3):
  22517.             _.s.talon.dealRow(flip = 0, frames = 0)
  22518.         
  22519.         for i in range(6):
  22520.             _.s.talon.dealRow(frames = 0)
  22521.         
  22522.         _.startDealSample()
  22523.         _.s.talon.dealRow()
  22524.         _.s.talon.dealCards()
  22525.  
  22526.     
  22527.     def getHighlightPilesStacks(_):
  22528.         return ()
  22529.  
  22530.  
  22531. registerGame(GameInfo(201, Sanibel, 'Sanibel', GI.GT_YUKON | GI.GT_CONTRIB | GI.GT_ORIGINAL, 2, 0))
  22532.  
  22533. class Flower_FoundationStack(AbstractFoundationStack):
  22534.     
  22535.     def __init__(_, x, y, game, suit, **cap):
  22536.         kwdefault(cap, max_cards = 12, max_move = 0, base_rank = ANY_RANK)
  22537.         apply(AbstractFoundationStack.__init__, (_, x, y, game, suit), cap)
  22538.  
  22539.     
  22540.     def isOonsooSequence(_, s):
  22541.         for i in range(len(s) - 1):
  22542.             if s[i].rank == 10:
  22543.                 (a, b) = (s[i].suit, s[i + 1].suit)
  22544.                 if b == 0:
  22545.                     b = 4
  22546.                 
  22547.             else:
  22548.                 (a, b) = _.swapTrashCards(s[i], s[i + 1])
  22549.             if a + 1 != b:
  22550.                 return 0
  22551.             
  22552.         
  22553.         return cardsFaceUp(s)
  22554.  
  22555.     
  22556.     def swapTrashCards(_, carda, cardb):
  22557.         (a, b) = (carda.suit, cardb.suit)
  22558.         if a == 0 and b == 3 and a == 3 and b == 0:
  22559.             (a, b) = (0, 1)
  22560.         elif a == 2 and b == 0 and a == 1 and b == 0:
  22561.             b = 3
  22562.         
  22563.         return (a, b)
  22564.  
  22565.  
  22566.  
  22567. class FlowerClock_Foundation(Flower_FoundationStack):
  22568.     
  22569.     def acceptsCards(_, from_stack, cards):
  22570.         if not _.basicAcceptsCards(from_stack, cards):
  22571.             return 0
  22572.         
  22573.         stackcards = _.cards
  22574.         if not stackcards:
  22575.             return cards[0].suit == 0
  22576.         
  22577.         if not (cards[0].rank == stackcards[-1].rank):
  22578.             return 0
  22579.         
  22580.         i = cards[0].suit
  22581.         j = stackcards[-1].suit
  22582.         if j == 0:
  22583.             j = 4
  22584.         
  22585.         return i + 1 == j
  22586.  
  22587.  
  22588.  
  22589. class Gaji_Foundation(Flower_FoundationStack):
  22590.     
  22591.     def __init__(_, x, y, game, suit, **cap):
  22592.         apply(Flower_FoundationStack.__init__, (_, x, y, game, suit), cap)
  22593.         _.CARD_YOFFSET = _.game.app.images.CARD_YOFFSET
  22594.  
  22595.     
  22596.     def acceptsCards(_, from_stack, cards):
  22597.         if not _.basicAcceptsCards(from_stack, cards):
  22598.             return 0
  22599.         
  22600.         stackcards = _.cards
  22601.         if stackcards[-1].suit == cards[0].suit:
  22602.             pass
  22603.         return (stackcards[-1].rank + 1) % 12 == cards[0].rank
  22604.  
  22605.  
  22606.  
  22607. class Pagoda_Foundation(Flower_FoundationStack):
  22608.     
  22609.     def acceptsCards(_, from_stack, cards):
  22610.         if not _.basicAcceptsCards(from_stack, cards):
  22611.             return 0
  22612.         
  22613.         stackcards = _.cards
  22614.         if not stackcards:
  22615.             if cards[0].suit == 0:
  22616.                 pass
  22617.             return cards[0].rank == _.id
  22618.         
  22619.         if not (cards[0].rank == stackcards[-1].rank):
  22620.             return 0
  22621.         
  22622.         i = cards[0].suit
  22623.         j = stackcards[-1].suit
  22624.         if j == 0:
  22625.             j = 4
  22626.         
  22627.         if len(stackcards) < 4:
  22628.             return i == j - 1
  22629.         elif len(stackcards) > 4:
  22630.             if i == 0:
  22631.                 i = 4
  22632.             
  22633.             return i == j + 1
  22634.         else:
  22635.             return i == j
  22636.  
  22637.  
  22638.  
  22639. class Samuri_Foundation(Flower_FoundationStack):
  22640.     
  22641.     def __init__(_, x, y, game, suit, **cap):
  22642.         apply(Flower_FoundationStack.__init__, (_, x, y, game, suit), cap)
  22643.         _.CARD_YOFFSET = -(_.game.app.images.CARD_YOFFSET)
  22644.  
  22645.     
  22646.     def acceptsCards(_, from_stack, cards):
  22647.         if not _.basicAcceptsCards(from_stack, cards):
  22648.             return 0
  22649.         
  22650.         stackcards = _.cards
  22651.         if not stackcards:
  22652.             if cards[0].suit == 0:
  22653.                 pass
  22654.             return cards[0].rank == _.id
  22655.         
  22656.         i = stackcards[-1].suit
  22657.         if i == 0:
  22658.             i = 4
  22659.         
  22660.         if cards[0].rank == stackcards[-1].rank:
  22661.             pass
  22662.         return cards[0].suit == i - 1
  22663.  
  22664.     
  22665.     def getBottomImage(_):
  22666.         return _.game.app.images.getTalonBottom()
  22667.  
  22668.  
  22669.  
  22670. class MatsuKiri_Foundation(Flower_FoundationStack):
  22671.     
  22672.     def __init__(_, x, y, game, suit, **cap):
  22673.         kwdefault(cap, max_cards = 48, min_accept = 4, max_accept = 4)
  22674.         apply(AbstractFoundationStack.__init__, (_, x, y, game, suit), cap)
  22675.         _.CARD_YOFFSET = _.game.app.images.CARDH / 10
  22676.  
  22677.     
  22678.     def acceptsCards(_, from_stack, cards):
  22679.         if not _.basicAcceptsCards(from_stack, cards):
  22680.             return 0
  22681.         
  22682.         stackcards = _.cards
  22683.         if not _.isOonsooSequence(cards):
  22684.             return 0
  22685.         
  22686.         if not stackcards:
  22687.             if cards[0].suit == 1:
  22688.                 pass
  22689.             return cards[0].rank == 0
  22690.         
  22691.         return stackcards[-1].rank + 1 == cards[0].rank
  22692.  
  22693.     
  22694.     def getBottomImage(_):
  22695.         return _.game.app.images.getLetter(0)
  22696.  
  22697.  
  22698.  
  22699. class GreatWall_Foundation(Flower_FoundationStack):
  22700.     
  22701.     def __init__(_, x, y, game, suit, **cap):
  22702.         kwdefault(cap, max_cards = 36, min_accept = 12, max_accept = 12)
  22703.         apply(Flower_FoundationStack.__init__, (_, x, y, game, suit), cap)
  22704.         _.CARD_YOFFSET = 0
  22705.  
  22706.     
  22707.     def acceptsCards(_, from_stack, cards):
  22708.         if not _.basicAcceptsCards(from_stack, cards):
  22709.             return 0
  22710.         
  22711.         stackcards = _.cards
  22712.         if cards[0].rank != 0:
  22713.             return 0
  22714.         
  22715.         for i in range(12):
  22716.             pass
  22717.         
  22718.         return isRankSequence(cards, dir = 1)
  22719.  
  22720.  
  22721.  
  22722. class GreatWall_BuildStack(Flower_FoundationStack):
  22723.     
  22724.     def __init__(_, x, y, game, suit, **cap):
  22725.         kwdefault(cap, max_cards = 12, max_move = 12)
  22726.         apply(Flower_FoundationStack.__init__, (_, x, y, game, suit), cap)
  22727.         _.CARD_YOFFSET = _.game.app.images.CARDH / 9
  22728.  
  22729.     
  22730.     def acceptsCards(_, from_stack, cards):
  22731.         if not _.basicAcceptsCards(from_stack, cards):
  22732.             return 0
  22733.         
  22734.         stackcards = _.cards
  22735.         if stackcards:
  22736.             if cards[0].rank != stackcards[-1].rank + 1:
  22737.                 return 0
  22738.             
  22739.         elif cards[0].rank != 0:
  22740.             return 0
  22741.         
  22742.         for c in cards:
  22743.             pass
  22744.         
  22745.         return isRankSequence(cards, dir = 1)
  22746.  
  22747.  
  22748.  
  22749. class FourWinds_Foundation(Flower_FoundationStack):
  22750.     
  22751.     def acceptsCards(_, from_stack, cards):
  22752.         if not _.basicAcceptsCards(from_stack, cards):
  22753.             return 0
  22754.         
  22755.         stackcards = _.cards
  22756.         if cards[0].suit != _.id:
  22757.             return 0
  22758.         
  22759.         if not stackcards:
  22760.             return cards[0].rank == 0
  22761.         else:
  22762.             return stackcards[-1].rank + 1 == cards[0].rank
  22763.  
  22764.  
  22765.  
  22766. class Flower_OpenStack(OpenStack):
  22767.     
  22768.     def __init__(_, x, y, game, yoffset, **cap):
  22769.         kwdefault(cap, max_move = 999999, max_accept = 999999)
  22770.         apply(OpenStack.__init__, (_, x, y, game), cap)
  22771.         _.CARD_YOFFSET = yoffset
  22772.  
  22773.     
  22774.     def isOonsooSequence(_, s):
  22775.         for i in range(len(s) - 1):
  22776.             if s[i].rank == 10:
  22777.                 (a, b) = (s[i].suit, s[i + 1].suit)
  22778.                 if b == 0:
  22779.                     b = 4
  22780.                 
  22781.             else:
  22782.                 (a, b) = _.swapTrashCards(s[i], s[i + 1])
  22783.             if a + 1 != b:
  22784.                 return 0
  22785.             
  22786.         
  22787.         return 1
  22788.  
  22789.     
  22790.     def swapTrashCards(_, carda, cardb):
  22791.         (a, b) = (carda.suit, cardb.suit)
  22792.         if a == 0 and b == 3 and a == 3 and b == 0:
  22793.             (a, b) = (3, 4)
  22794.         elif a == 2 and b == 0 and a == 0 and b == 1:
  22795.             b = 3
  22796.         
  22797.         return (a, b)
  22798.  
  22799.  
  22800.  
  22801. class FlowerClock_RowStack(Flower_OpenStack):
  22802.     
  22803.     def acceptsCards(_, from_stack, cards):
  22804.         if not _.basicAcceptsCards(from_stack, cards):
  22805.             return 0
  22806.         
  22807.         stackcards = _.cards
  22808.         if not stackcards:
  22809.             return 1
  22810.         
  22811.         return (stackcards[-1].suit + 1) % 4 == cards[0].suit
  22812.  
  22813.  
  22814.  
  22815. class Gaji_RowStack(Flower_OpenStack):
  22816.     
  22817.     def acceptsCards(_, from_stack, cards):
  22818.         if not _.basicAcceptsCards(from_stack, cards):
  22819.             return 0
  22820.         
  22821.         stackcards = _.cards
  22822.         if not stackcards:
  22823.             return 1
  22824.         elif cards[0].suit == 0 and cards[0].rank == 10 and stackcards[-1].suit == 0 and stackcards[-1].rank == 10:
  22825.             return 1
  22826.         elif cards[0].rank != stackcards[-1].rank:
  22827.             return 0
  22828.         
  22829.         (a, b) = _.swapTrashCards(stackcards[-1], cards[0])
  22830.         return a + 1 == b
  22831.  
  22832.  
  22833.  
  22834. class Matsukiri_RowStack(Flower_OpenStack):
  22835.     
  22836.     def acceptsCards(_, from_stack, cards):
  22837.         if not _.basicAcceptsCards(from_stack, cards):
  22838.             return 0
  22839.         
  22840.         stackcards = _.cards
  22841.         if not stackcards:
  22842.             return cards[0].suit == 1
  22843.         
  22844.         return _.isOonsooSequence([
  22845.             stackcards[-1],
  22846.             cards[0]])
  22847.  
  22848.  
  22849.  
  22850. class Oonsoo_RowStack(Flower_OpenStack):
  22851.     
  22852.     def acceptsCards(_, from_stack, cards):
  22853.         if not _.basicAcceptsCards(from_stack, cards):
  22854.             return 0
  22855.         
  22856.         stackcards = _.cards
  22857.         if not _.isOonsooSequence(cards):
  22858.             return 0
  22859.         
  22860.         if not stackcards:
  22861.             return cards[0].suit == 1
  22862.         
  22863.         if stackcards[-1].rank != cards[0].rank:
  22864.             return 0
  22865.         
  22866.         return _.isOonsooSequence([
  22867.             stackcards[-1],
  22868.             cards[0]])
  22869.  
  22870.  
  22871.  
  22872. class Samuri_RowStack(Flower_OpenStack):
  22873.     
  22874.     def acceptsCards(_, from_stack, cards):
  22875.         if not _.basicAcceptsCards(from_stack, cards):
  22876.             return 0
  22877.         
  22878.         stackcards = _.cards
  22879.         if not stackcards:
  22880.             return cards[0].suit == 1
  22881.         
  22882.         i = cards[0].suit
  22883.         if i == 0:
  22884.             i = 4
  22885.         
  22886.         if stackcards[-1].rank == cards[0].rank:
  22887.             pass
  22888.         return stackcards[-1].suit == i - 1
  22889.  
  22890.  
  22891.  
  22892. class GreatWall_RowStack(Flower_OpenStack):
  22893.     
  22894.     def acceptsCards(_, from_stack, cards):
  22895.         if not _.basicAcceptsCards(from_stack, cards):
  22896.             return 0
  22897.         
  22898.         stackcards = _.cards
  22899.         if not stackcards:
  22900.             return cards[0].suit == 1
  22901.         
  22902.         if cards[0].suit == stackcards[-1].suit:
  22903.             return (cards[0].rank + 1) % 12 == stackcards[-1].rank
  22904.         
  22905.         (a, b) = _.swapTrashCards(stackcards[-1], cards[0])
  22906.         return a + 1 == b
  22907.  
  22908.  
  22909.  
  22910. class FourWinds_RowStack(Flower_OpenStack):
  22911.     
  22912.     def acceptsCards(_, from_stack, cards):
  22913.         if not _.basicAcceptsCards(from_stack, cards):
  22914.             return 0
  22915.         
  22916.         stackcards = _.cards
  22917.         if not stackcards:
  22918.             return 1
  22919.         
  22920.         if cards[0].suit == stackcards[-1].suit:
  22921.             pass
  22922.         return cards[0].rank + 1 == stackcards[-1].rank
  22923.  
  22924.     
  22925.     def getBottomImage(_):
  22926.         return _.game.app.images.getReserveBottom()
  22927.  
  22928.  
  22929.  
  22930. class AbstractFlowerGame(Game):
  22931.     SUITS = ('Pine', 'Plum', 'Cherry', 'Wisteria', 'Iris', 'Rose', 'Clover', 'Moon', 'Mum', 'Maple', 'Rain', 'Phoenix')
  22932.     
  22933.     def shallHighlightMatch(_, stack1, card1, stack2, card2):
  22934.         if card1.rank != card2.rank:
  22935.             return 0
  22936.         
  22937.         (a, b) = (card1.suit, card2.suit)
  22938.         if a == 0:
  22939.             a = 4
  22940.         elif b == 0:
  22941.             b = 4
  22942.         
  22943.         if not a + 1 == b:
  22944.             pass
  22945.         return a - 1 == b
  22946.  
  22947.  
  22948.  
  22949. class FlowerClock(AbstractFlowerGame):
  22950.     
  22951.     def createGame(_):
  22952.         (l, s) = (Layout(_), _.s)
  22953.         font = getFont('canvas_card', cardw = l.CW)
  22954.         _.setSize(l.XM + l.XS * 10.5, l.YM + l.YS * 5.5)
  22955.         xoffset = (1, 2, 2.5, 2, 1, 0, -1, -2, -2.5, -2, -1, 0)
  22956.         yoffset = (0.25, 0.75, 1.9, 3, 3.5, 3.75, 3.5, 3, 1.9, 0.75, 0.25, 0)
  22957.         x = l.XM + l.XS * 7
  22958.         y = l.CH / 3
  22959.         for i in range(12):
  22960.             x0 = x + xoffset[i] * l.XS
  22961.             y0 = y + yoffset[i] * l.YS
  22962.             s.foundations.append(FlowerClock_Foundation(x0, y0, _, ANY_SUIT))
  22963.             t = MfxCanvasText(_.canvas, x0 + l.CW / 2, y0 + l.YS, anchor = 'center', font = font, text = _.SUITS[i])
  22964.         
  22965.         for j in range(2):
  22966.             (x, y) = (l.XM, l.YM + l.YS * j * 2.7)
  22967.             for i in range(4):
  22968.                 s.rows.append(FlowerClock_RowStack(x, y, _, yoffset = l.CH / 4, max_cards = 8))
  22969.                 x = x + l.XS
  22970.             
  22971.         
  22972.         _.setRegion(s.rows, (0, 0, l.XS * 4, 999999))
  22973.         s.talon = InitialDealTalonStack(_.width - l.XS, _.height - l.YS, _)
  22974.         l.defaultStackGroups()
  22975.  
  22976.     
  22977.     def startGame(_):
  22978.         if not __debug__ and len(_.s.talon.cards) == 48:
  22979.             raise AssertionError
  22980.         for i in range(5):
  22981.             _.s.talon.dealRow(frames = 0)
  22982.         
  22983.         _.startDealSample()
  22984.         _.s.talon.dealRow()
  22985.         if not __debug__ and len(_.s.talon.cards) == 0:
  22986.             raise AssertionError
  22987.         0
  22988.  
  22989.     
  22990.     def isGameWon(_):
  22991.         for i in _.s.foundations:
  22992.             if i.cards[0].rank != i.id:
  22993.                 return 0
  22994.             
  22995.         
  22996.         return 1
  22997.  
  22998.     
  22999.     def shallHighlightMatch(_, stack1, card1, stack2, card2):
  23000.         return 0
  23001.  
  23002.     
  23003.     def getAutoStacks(_, event = None):
  23004.         if event is None:
  23005.             return (_.sg.dropstacks, (), _.sg.dropstacks)
  23006.         else:
  23007.             return (_.sg.dropstacks, _.sg.dropstacks, _.sg.dropstacks)
  23008.  
  23009.  
  23010.  
  23011. class Gaji(AbstractFlowerGame):
  23012.     
  23013.     def createGame(_):
  23014.         (l, s) = (Layout(_), _.s)
  23015.         _.setSize(l.XM * 7 + l.XS * 12, l.YM * 2 + l.YS * 6)
  23016.         x = l.XM
  23017.         y = l.YM
  23018.         s.foundations.append(Gaji_Foundation(x, y, _, 1))
  23019.         x = x + l.XS + 10
  23020.         s.foundations.append(Gaji_Foundation(x, y, _, 2))
  23021.         x = x + l.XS + 20
  23022.         for i in range(8):
  23023.             s.rows.append(Gaji_RowStack(x, y, _, yoffset = l.CH / 2, max_cards = 12))
  23024.             x = x + l.XS
  23025.         
  23026.         _.setRegion(s.rows, (l.XM + l.XS * 2 + 11, -999, l.XM + l.XS * 10 + 20, 999999))
  23027.         x = x + 20
  23028.         s.foundations.append(Gaji_Foundation(x, y, _, 3))
  23029.         x = x + l.XS + 10
  23030.         s.foundations.append(Gaji_Foundation(x, y, _, 0))
  23031.         s.talon = InitialDealTalonStack(_.width - l.XS, _.height - l.YS, _)
  23032.         l.defaultStackGroups()
  23033.  
  23034.     
  23035.     def _shuffleHook(_, cards):
  23036.         topcards = [
  23037.             None,
  23038.             None,
  23039.             None,
  23040.             None]
  23041.         for c in cards[:]:
  23042.             if not topcards[c.suit]:
  23043.                 if c.rank == 10:
  23044.                     pass
  23045.                 if not (c.suit == 0):
  23046.                     topcards[c.suit] = c
  23047.                     cards.remove(c)
  23048.                 
  23049.             
  23050.         
  23051.         return topcards + cards
  23052.  
  23053.     
  23054.     def startGame(_):
  23055.         if not __debug__ and len(_.s.talon.cards) == 48:
  23056.             raise AssertionError
  23057.         for i in range(4):
  23058.             _.s.talon.dealRow(frames = 0)
  23059.         
  23060.         _.startDealSample()
  23061.         r = _.s.rows
  23062.         _.s.talon.dealRow(rows = (r[0], r[1], r[2], r[5], r[6], r[7]))
  23063.         _.s.talon.dealRow(rows = (r[0], r[1], r[6], r[7]))
  23064.         _.s.talon.dealRow(rows = (r[0], r[7]))
  23065.         r = _.s.foundations
  23066.         _.s.talon.dealRow(rows = (r[2], r[1], r[0], r[3]))
  23067.         if not __debug__ and len(_.s.talon.cards) == 0:
  23068.             raise AssertionError
  23069.         0
  23070.  
  23071.     
  23072.     def fillStack(_, stack):
  23073.         if stack in _.s.rows:
  23074.             if stack.cards and not (stack.cards[-1].face_up):
  23075.                 _.flipMove(stack)
  23076.             
  23077.         
  23078.  
  23079.  
  23080.  
  23081. class Oonsoo(AbstractFlowerGame):
  23082.     
  23083.     def createGame(_):
  23084.         (l, s) = (Layout(_), _.s)
  23085.         _.setSize(l.XM * 2 + l.XS * 8, l.YM + l.YS * 6)
  23086.         x = l.XM + l.XS * 1.5
  23087.         y = l.YM
  23088.         for i in range(6):
  23089.             s.rows.append(Oonsoo_RowStack(x, y, _, yoffset = l.YOFFSET))
  23090.             x = x + l.XS * 1.1
  23091.         
  23092.         y = y + l.YS * 2.5
  23093.         x = x - l.XS * 6.6
  23094.         for i in range(6):
  23095.             s.rows.append(Oonsoo_RowStack(x, y, _, yoffset = l.YOFFSET))
  23096.             x = x + l.XS * 1.1
  23097.         
  23098.         x = l.XM
  23099.         y = l.YM
  23100.         s.talon = DealRowTalonStack(x, y, _)
  23101.         l.createText(s.talon, 'ss')
  23102.         l.defaultStackGroups()
  23103.  
  23104.     
  23105.     def startGame(_):
  23106.         if not __debug__ and len(_.s.talon.cards) == 48:
  23107.             raise AssertionError
  23108.         _.s.talon.dealRow(flip = 0, frames = 0)
  23109.         _.startDealSample()
  23110.         _.s.talon.dealCards()
  23111.  
  23112.     
  23113.     def isGameWon(_):
  23114.         if _.s.talon.cards:
  23115.             return 0
  23116.         
  23117.         for s in _.s.rows:
  23118.             pass
  23119.         
  23120.         return 1
  23121.  
  23122.     
  23123.     def fillStack(_, stack):
  23124.         if stack in _.s.rows:
  23125.             if stack.cards and not (stack.cards[-1].face_up):
  23126.                 _.flipMove(stack)
  23127.             
  23128.         
  23129.  
  23130.  
  23131.  
  23132. class Pagoda(AbstractFlowerGame):
  23133.     
  23134.     def createGame(_):
  23135.         (l, s) = (Layout(_), _.s)
  23136.         font = getFont('canvas_card', cardw = l.CW)
  23137.         _.setSize(l.XM + l.XS * 11, l.YS * 6)
  23138.         _.foundation_texts = []
  23139.         for j in range(4):
  23140.             (x, y) = (l.XM + l.XS * 8, l.YM * 3 + l.YS * j * 1.5)
  23141.             for i in range(3):
  23142.                 s.foundations.append(Pagoda_Foundation(x, y, _, ANY_SUIT))
  23143.                 t = MfxCanvasText(_.canvas, x + l.CW / 2, y - 12, anchor = 'center', font = font)
  23144.                 _.foundation_texts.append(t)
  23145.                 x = x + l.XS
  23146.             
  23147.         
  23148.         (x, y) = (l.XM + l.XS, l.YM)
  23149.         d = (0.4, 0.25, 0, 0.25, 0.4)
  23150.         for i in range(5):
  23151.             s.reserves.append(ReserveStack(x + l.XS * i, y + l.YS * d[i], _))
  23152.         
  23153.         (x, y) = (l.XM + l.XS * 2, y + l.YS * 1.1)
  23154.         d = (0.25, 0, 0.25)
  23155.         for i in range(3):
  23156.             s.reserves.append(ReserveStack(x + l.XS * i, y + l.YS * d[i], _))
  23157.         
  23158.         (x, y) = (l.XM, y + l.YS * 1.1)
  23159.         d = (0.5, 0.4, 0.25, 0, 0.25, 0.4, 0.5)
  23160.         for i in range(7):
  23161.             s.reserves.append(ReserveStack(x + l.XS * i, y + l.YS * d[i], _))
  23162.         
  23163.         (x, y) = (l.XM + l.XS, y + l.YS * 1.5)
  23164.         for i in range(5):
  23165.             s.reserves.append(ReserveStack(x + l.XS * i, y, _))
  23166.         
  23167.         x = l.XM + l.XS * 2.5
  23168.         y = l.YM + l.YS * 4.9
  23169.         s.talon = WasteTalonStack(x, y, _, num_deal = 4, max_rounds = 1)
  23170.         l.createText(s.talon, 'sw')
  23171.         x = x + l.XS
  23172.         s.waste = WasteStack(x, y, _)
  23173.         l.createText(s.waste, 'se')
  23174.         l.defaultStackGroups()
  23175.  
  23176.     
  23177.     def updateText(_):
  23178.         if _.preview > 1:
  23179.             return None
  23180.         
  23181.         for i in range(12):
  23182.             s = _.s.foundations[i]
  23183.             if not (s.cards) or len(s.cards) == 8:
  23184.                 text = _.SUITS[i]
  23185.             elif len(s.cards) < 5:
  23186.                 text = 'Rising'
  23187.             else:
  23188.                 text = 'Setting'
  23189.             _.foundation_texts[i].config(text = text)
  23190.         
  23191.  
  23192.     
  23193.     def startGame(_):
  23194.         if not __debug__ and len(_.s.talon.cards) == 48 * 2:
  23195.             raise AssertionError
  23196.         _.updateText()
  23197.         _.startDealSample()
  23198.         _.s.talon.dealRow(rows = _.s.reserves)
  23199.         _.s.talon.dealCards()
  23200.  
  23201.     
  23202.     def fillStack(_, stack):
  23203.         if not (stack.cards) and stack is _.s.waste:
  23204.             if _.canDealCards():
  23205.                 _.dealCards()
  23206.             
  23207.         
  23208.  
  23209.  
  23210.  
  23211. class MatsuKiri(AbstractFlowerGame):
  23212.     
  23213.     def createGame(_):
  23214.         (l, s) = (Layout(_), _.s)
  23215.         _.setSize(l.XM * 3 + l.XS * 9, l.YM + l.YS * 6)
  23216.         x = l.XM
  23217.         y = l.YM
  23218.         for i in range(8):
  23219.             s.rows.append(Matsukiri_RowStack(x, y, _, yoffset = l.CH / 2, max_cards = 12))
  23220.             x = x + l.XS
  23221.         
  23222.         _.setRegion(s.rows, (-999, -999, l.XM + l.XS * 8 + 10, 999999))
  23223.         x = x + l.XM * 2
  23224.         s.foundations.append(MatsuKiri_Foundation(x, y, _, ANY_SUIT))
  23225.         _.setRegion(s.foundations, (l.XM + l.XS * 8 + 10, -999, 999999, 999999))
  23226.         s.talon = InitialDealTalonStack(_.width - l.XS, _.height - l.YS, _)
  23227.         l.defaultStackGroups()
  23228.  
  23229.     
  23230.     def startGame(_):
  23231.         if not __debug__ and len(_.s.talon.cards) == 48:
  23232.             raise AssertionError
  23233.         for i in range(5):
  23234.             _.s.talon.dealRow(frames = 0)
  23235.         
  23236.         _.startDealSample()
  23237.         _.s.talon.dealRow()
  23238.         if not __debug__ and len(_.s.talon.cards) == 0:
  23239.             raise AssertionError
  23240.         0
  23241.  
  23242.     
  23243.     def fillStack(_, stack):
  23244.         if stack in _.s.rows:
  23245.             if len(stack.cards) > 0 and not (stack.cards[-1].face_up):
  23246.                 _.flipMove(stack)
  23247.             
  23248.         
  23249.  
  23250.  
  23251.  
  23252. class Samuri(AbstractFlowerGame):
  23253.     
  23254.     def createGame(_):
  23255.         (l, s) = (Layout(_), _.s)
  23256.         font = getFont('canvas_card', cardw = l.CW)
  23257.         _.setSize(l.XM * 3 + l.XS * 11, l.YM + l.YS * 6)
  23258.         n = 0
  23259.         for i in range(3):
  23260.             x = l.XM
  23261.             y = (l.YS - l.YM) + l.YS * i * 2
  23262.             for j in range(2):
  23263.                 s.foundations.append(Samuri_Foundation(x, y, _, ANY_SUIT))
  23264.                 t = MfxCanvasText(_.canvas, x + l.CW / 2, y + l.YS + 5, anchor = 'center', font = font, text = _.SUITS[n])
  23265.                 x = x + l.XS
  23266.                 n = n + 1
  23267.             
  23268.         
  23269.         for i in range(3):
  23270.             x = l.XM * 3 + l.XS * 9
  23271.             y = (l.YS - l.YM) + l.YS * i * 2
  23272.             for j in range(2):
  23273.                 s.foundations.append(Samuri_Foundation(x, y, _, ANY_SUIT))
  23274.                 t = MfxCanvasText(_.canvas, x + l.CW / 2, y + l.YS + 5, anchor = 'center', font = font, text = _.SUITS[n])
  23275.                 x = x + l.XS
  23276.                 n = n + 1
  23277.             
  23278.         
  23279.         x = l.XM * 2 + l.XS * 2
  23280.         y = l.YM
  23281.         for i in range(7):
  23282.             s.rows.append(Samuri_RowStack(x, y, _, yoffset = l.CH / 4, max_cards = 8))
  23283.             x = x + l.XS
  23284.         
  23285.         _.setRegion(s.rows, (l.XM + l.XS * 2, -999, l.XM * 2 + l.XS * 9, 999999))
  23286.         x = l.XM * 2 + l.XS * 4.5
  23287.         y = _.height - l.YS * 2
  23288.         s.talon = WasteTalonStack(x, y, _, num_deal = 1, max_rounds = 1)
  23289.         l.createText(s.talon, 'sw')
  23290.         x = x + l.XS
  23291.         s.waste = WasteStack(x, y, _)
  23292.         l.createText(s.waste, 'se')
  23293.         l.defaultStackGroups()
  23294.  
  23295.     
  23296.     def startGame(_):
  23297.         if not __debug__ and len(_.s.talon.cards) == 48:
  23298.             raise AssertionError
  23299.         _.s.talon.dealRow(flip = 0, frames = 0)
  23300.         for i in range(len(_.s.rows)):
  23301.             _.s.talon.dealRow(rows = _.s.rows[i:7 - i], flip = 0, frames = 0)
  23302.         
  23303.         _.startDealSample()
  23304.         _.s.talon.dealRow()
  23305.         _.s.talon.dealCards()
  23306.  
  23307.     
  23308.     def fillStack(_, stack):
  23309.         if not (stack.cards) and stack is _.s.waste:
  23310.             if _.canDealCards():
  23311.                 _.dealCards()
  23312.             
  23313.         
  23314.         if stack in _.s.rows:
  23315.             if len(stack.cards) and not (stack.cards[-1].face_up):
  23316.                 _.flipMove(stack)
  23317.             
  23318.         
  23319.  
  23320.  
  23321.  
  23322. class GreatWall(AbstractFlowerGame):
  23323.     
  23324.     def createGame(_):
  23325.         (l, s) = (Layout(_), _.s)
  23326.         font = getFont('canvas_card', cardw = l.CW)
  23327.         _.setSize(l.XM + l.XS * 15, l.YM + l.YS * 6.2)
  23328.         _.foundation_texts = []
  23329.         x = l.XM
  23330.         y = l.YM
  23331.         for i in range(2):
  23332.             s.foundations.append(GreatWall_Foundation(x, y, _, i + 1))
  23333.             _.foundation_texts.append(MfxCanvasText(_.canvas, x + l.CW + 4, y + l.CH / 2, anchor = 'w', font = font))
  23334.             y = y + l.YS * 1
  23335.         
  23336.         x = _.width - l.XS
  23337.         y = l.YM
  23338.         for i in range(2):
  23339.             s.foundations.append(GreatWall_Foundation(x, y, _, (i + 3) % 4))
  23340.             _.foundation_texts.append(MfxCanvasText(_.canvas, x - 4, y + l.CH / 2, anchor = 'e', font = font))
  23341.             y = y + l.YS * 1
  23342.         
  23343.         x = l.XM
  23344.         y = l.YM + l.YS * 2.2
  23345.         for i in range(2):
  23346.             s.foundations.append(GreatWall_BuildStack(x, y, _, i + 1))
  23347.             _.foundation_texts.append(MfxCanvasText(_.canvas, x + l.CW + 4, y + l.CH / 2, anchor = 'w', font = font))
  23348.             y = y + l.YS * 2
  23349.         
  23350.         x = _.width - l.XS
  23351.         y = l.YM + l.YS * 2.2
  23352.         for i in range(2):
  23353.             s.foundations.append(GreatWall_BuildStack(x, y, _, (i + 3) % 4))
  23354.             _.foundation_texts.append(MfxCanvasText(_.canvas, x - 4, y + l.CH / 2, anchor = 'e', font = font))
  23355.             y = y + l.YS * 2
  23356.         
  23357.         x = l.XM + l.XS * 1.5
  23358.         y = l.YM
  23359.         for i in range(12):
  23360.             s.rows.append(GreatWall_RowStack(x, y, _, yoffset = l.CH / 4, max_cards = 26))
  23361.             x = x + l.XS
  23362.         
  23363.         _.setRegion(s.rows, (l.XM + l.XS * 1.25, -999, _.width - l.XS * 1.25, 999999))
  23364.         x = _.width / 2 - l.CW / 2
  23365.         y = _.height - l.YS * 1.2
  23366.         s.talon = InitialDealTalonStack(x, y, _)
  23367.         l.defaultStackGroups()
  23368.  
  23369.     
  23370.     def updateText(_):
  23371.         if _.preview > 1:
  23372.             return None
  23373.         
  23374.         for i in range(8):
  23375.             if t == 0:
  23376.                 t = ''
  23377.             elif t == f:
  23378.                 t = 'Full'
  23379.             
  23380.             _.foundation_texts[i].config(text = str(t))
  23381.         
  23382.  
  23383.     
  23384.     def startGame(_):
  23385.         if not __debug__ and len(_.s.talon.cards) == 48 * 4:
  23386.             raise AssertionError
  23387.         _.updateText()
  23388.         for i in range(15):
  23389.             _.s.talon.dealRow(flip = 0, frames = 0)
  23390.         
  23391.         _.startDealSample()
  23392.         _.s.talon.dealRow()
  23393.         if not __debug__ and len(_.s.talon.cards) == 0:
  23394.             raise AssertionError
  23395.         0
  23396.  
  23397.     
  23398.     def fillStack(_, stack):
  23399.         if stack in _.s.rows:
  23400.             if stack.cards and not (stack.cards[-1].face_up):
  23401.                 _.flipMove(stack)
  23402.             
  23403.         
  23404.  
  23405.     
  23406.     def shallHighlightMatch(_, stack1, card1, stack2, card2):
  23407.         if not card1.suit == card2.suit and (card1.rank + 1) % 12 == card2.rank:
  23408.             pass
  23409.         return (card1.rank - 1) % 12 == card2.rank
  23410.  
  23411.  
  23412.  
  23413. class FourWinds(AbstractFlowerGame):
  23414.     
  23415.     def createGame(_):
  23416.         (l, s) = (Layout(_), _.s)
  23417.         font = getFont('canvas_card', cardw = l.CW)
  23418.         _.setSize(7 * l.XS, 5 * l.YS + 3 * l.YM)
  23419.         TEXTS = ('North', 'East', 'South', 'West', 'NW', 'NE', 'SE', 'SW')
  23420.         x = l.XM * 3
  23421.         y = l.YM
  23422.         xoffset = (0, 2.5, 5, 2.5)
  23423.         yoffset = (2, 0, 2, 4)
  23424.         for i in range(4):
  23425.             x0 = x + xoffset[i] * l.XS
  23426.             y0 = y + yoffset[i] * l.YS
  23427.             s.foundations.append(FourWinds_Foundation(x0, y0, _, i))
  23428.             t = MfxCanvasText(_.canvas, x0 + l.CW / 2, y0 + l.YS + 5, anchor = 'center', font = font, text = TEXTS[i])
  23429.         
  23430.         xoffset = (1.25, 3.75, 3.75, 1.25)
  23431.         yoffset = (0.75, 0.75, 3, 3)
  23432.         for i in range(4):
  23433.             x0 = x + xoffset[i] * l.XS
  23434.             y0 = y + yoffset[i] * l.YS
  23435.             s.rows.append(FourWinds_RowStack(x0, y0, _, yoffset = 10, max_cards = 3, max_accept = 1))
  23436.             t = MfxCanvasText(_.canvas, x0 + l.CW / 2, y0 + l.YS + 5, anchor = 'center', font = font, text = TEXTS[i + 4])
  23437.         
  23438.         _.setRegion(s.rows, (x + l.XS, y + l.YS * 0.65, x + l.XS * 4 + 5, y + l.YS * 3 + 5))
  23439.         x = x + 2 * l.XS
  23440.         y = y + 2 * l.YS
  23441.         s.talon = WasteTalonStack(x, y, _, num_deal = 1, max_rounds = 2)
  23442.         l.createText(s.talon, 'ss')
  23443.         x = x + l.XS
  23444.         s.waste = WasteStack(x, y, _)
  23445.         l.createText(s.waste, 'ss')
  23446.         l.defaultStackGroups()
  23447.  
  23448.     
  23449.     def startGame(_):
  23450.         if not __debug__ and len(_.s.talon.cards) == 48:
  23451.             raise AssertionError
  23452.         _.startDealSample()
  23453.         _.s.talon.dealCards()
  23454.  
  23455.     
  23456.     def fillStack(_, stack):
  23457.         if not (stack.cards) and stack is _.s.waste:
  23458.             if _.canDealCards():
  23459.                 _.dealCards()
  23460.             
  23461.         
  23462.  
  23463.     
  23464.     def shallHighlightMatch(_, stack1, card1, stack2, card2):
  23465.         if not card1.suit == card2.suit and (card1.rank + 1) % 12 == card2.rank:
  23466.             pass
  23467.         return (card1.rank - 1) % 12 == card2.rank
  23468.  
  23469.  
  23470.  
  23471. def r(id, gameclass, name, game_type, decks, redeals):
  23472.     game_type = game_type | GI.GT_HANAFUDA | GI.GT_CONTRIB
  23473.     gi = GameInfo(id, gameclass, name, game_type, decks, redeals, ranks = range(12))
  23474.     registerGame(gi)
  23475.     return gi
  23476.  
  23477. r(348, FlowerClock, 'Flower Clock', GI.GT_HANAFUDA | GI.GT_OPEN, 1, 0)
  23478. r(347, Gaji, 'Gaji', GI.GT_HANAFUDA | GI.GT_OPEN, 1, 0)
  23479. r(345, Oonsoo, 'Oonsoo', GI.GT_HANAFUDA, 1, 0)
  23480. r(349, Pagoda, 'Pagoda', GI.GT_HANAFUDA, 2, 0)
  23481. r(346, MatsuKiri, 'MatsuKiri', GI.GT_HANAFUDA | GI.GT_OPEN, 1, 0)
  23482. r(350, Samuri, 'Samuri', GI.GT_HANAFUDA, 1, 0)
  23483. r(351, GreatWall, 'Great Wall', GI.GT_HANAFUDA, 4, 0)
  23484. r(352, FourWinds, 'Four Winds', GI.GT_HANAFUDA, 1, 1)
  23485. del r
  23486.  
  23487. class TowerOfHanoy_Hint(CautiousDefaultHint):
  23488.     pass
  23489.  
  23490.  
  23491. class TowerOfHanoy_RowStack(BasicRowStack):
  23492.     
  23493.     def acceptsCards(_, from_stack, cards):
  23494.         if not BasicRowStack.acceptsCards(_, from_stack, cards):
  23495.             return 0
  23496.         
  23497.         if not (_.cards):
  23498.             return 1
  23499.         
  23500.         return _.cards[-1].rank > cards[0].rank
  23501.  
  23502.     
  23503.     def getBottomImage(_):
  23504.         return _.game.app.images.getReserveBottom()
  23505.  
  23506.  
  23507.  
  23508. class TowerOfHanoy(Game):
  23509.     RowStack_Class = TowerOfHanoy_RowStack
  23510.     Hint_Class = TowerOfHanoy_Hint
  23511.     
  23512.     def createGame(_):
  23513.         (l, s) = (Layout(_), _.s)
  23514.         h = max(2 * l.YS, l.YS + (len(_.cards) - 1) * l.YOFFSET + l.YM)
  23515.         _.setSize(l.XM + 5 * l.XS, l.YM + l.YS + h)
  23516.         for i in range(3):
  23517.             (x, y) = (l.XM + (i + 1) * l.XS, l.YM)
  23518.             s.rows.append(_.RowStack_Class(x, y, _, max_accept = 1, max_move = 1))
  23519.         
  23520.         s.talon = InitialDealTalonStack(l.XM, _.height - l.YS, _)
  23521.         l.defaultStackGroups()
  23522.  
  23523.     
  23524.     def startGame(_):
  23525.         _.startDealSample()
  23526.         for i in range(3):
  23527.             _.s.talon.dealRow()
  23528.         
  23529.  
  23530.     
  23531.     def isGameWon(_):
  23532.         for s in _.s.rows:
  23533.             pass
  23534.         
  23535.         return 0
  23536.  
  23537.     
  23538.     def shallHighlightMatch(_, stack1, card1, stack2, card2):
  23539.         if not card1.rank + 1 == card2.rank:
  23540.             pass
  23541.         return card2.rank + 1 == card1.rank
  23542.  
  23543.     
  23544.     def getAutoStacks(_, event = None):
  23545.         return ((), (), _.sg.dropstacks)
  23546.  
  23547.  
  23548.  
  23549. class HanoiPuzzle_RowStack(TowerOfHanoy_RowStack):
  23550.     
  23551.     def getBottomImage(_):
  23552.         if _.id == len(_.game.s.rows) - 1:
  23553.             return _.game.app.images.getSuitBottom()
  23554.         
  23555.         return _.game.app.images.getReserveBottom()
  23556.  
  23557.  
  23558.  
  23559. class HanoiPuzzle4(TowerOfHanoy):
  23560.     RowStack_Class = HanoiPuzzle_RowStack
  23561.     
  23562.     def _shuffleHook(_, cards):
  23563.         return _._shuffleHookMoveToTop(cards, (lambda c: (1, -(c.id))))
  23564.  
  23565.     
  23566.     def startGame(_):
  23567.         _.startDealSample()
  23568.         for i in range(len(_.cards)):
  23569.             _.s.talon.dealRow(rows = _.s.rows[:1])
  23570.         
  23571.  
  23572.     
  23573.     def isGameWon(_):
  23574.         return len(_.s.rows[-1].cards) == len(_.cards)
  23575.  
  23576.  
  23577.  
  23578. class HanoiPuzzle5(HanoiPuzzle4):
  23579.     pass
  23580.  
  23581.  
  23582. class HanoiPuzzle6(HanoiPuzzle4):
  23583.     pass
  23584.  
  23585. registerGame(GameInfo(124, TowerOfHanoy, 'Tower of Hanoy', GI.GT_PUZZLE_TYPE, 1, 0, suits = (2,), ranks = range(9)))
  23586. registerGame(GameInfo(207, HanoiPuzzle4, 'Hanoi Puzzle 4', GI.GT_PUZZLE_TYPE, 1, 0, suits = (2,), ranks = range(4), rules_filename = 'hanoipuzzle.html'))
  23587. registerGame(GameInfo(208, HanoiPuzzle5, 'Hanoi Puzzle 5', GI.GT_PUZZLE_TYPE, 1, 0, suits = (2,), ranks = range(5), rules_filename = 'hanoipuzzle.html'))
  23588. registerGame(GameInfo(209, HanoiPuzzle6, 'Hanoi Puzzle 6', GI.GT_PUZZLE_TYPE, 1, 0, suits = (2,), ranks = range(6), rules_filename = 'hanoipuzzle.html'))
  23589.  
  23590. class HexADeck_FoundationStack(SS_FoundationStack):
  23591.     
  23592.     def __init__(_, x, y, game, suit, **cap):
  23593.         kwdefault(cap, max_move = 0, max_cards = 12)
  23594.         apply(SS_FoundationStack.__init__, (_, x, y, game, suit), cap)
  23595.  
  23596.  
  23597.  
  23598. class HexATrump_Foundation(HexADeck_FoundationStack):
  23599.     
  23600.     def acceptsCards(_, from_stack, cards):
  23601.         if not _.basicAcceptsCards(from_stack, cards):
  23602.             return 0
  23603.         
  23604.         for s in _.game.s.foundations[:3]:
  23605.             pass
  23606.         
  23607.         return 1
  23608.  
  23609.  
  23610.  
  23611. class HexADeck_OpenStack(OpenStack):
  23612.     
  23613.     def __init__(_, x, y, game, yoffset, **cap):
  23614.         kwdefault(cap, max_move = 999999)
  23615.         apply(OpenStack.__init__, (_, x, y, game), cap)
  23616.         _.CARD_YOFFSET = yoffset
  23617.  
  23618.  
  23619.  
  23620. class Bits_RowStack(ReserveStack):
  23621.     
  23622.     def acceptsCards(_, from_stack, cards):
  23623.         if not _.basicAcceptsCards(from_stack, cards):
  23624.             return 0
  23625.         
  23626.         stackcards = _.cards
  23627.         if stackcards or cards[0].suit == 4:
  23628.             return 0
  23629.         
  23630.         i = _.id / 4
  23631.         for r in _.game.s.rows[i * 4:_.id]:
  23632.             pass
  23633.         
  23634.         return (_.game.s.foundations[i].cards[-1].rank + 1 >> _.id % 4) % 2 == (cards[0].rank + 1) % 2
  23635.  
  23636.  
  23637.  
  23638. class Bytes_RowStack(ReserveStack):
  23639.     
  23640.     def acceptsCards(_, from_stack, cards):
  23641.         if not _.basicAcceptsCards(from_stack, cards):
  23642.             return 0
  23643.         
  23644.         stackcards = _.cards
  23645.         if stackcards or cards[0].suit == 4:
  23646.             return 0
  23647.         
  23648.         id = _.id - 16
  23649.         i = id / 2
  23650.         for r in _.game.s.rows[16 + i * 2:_.id]:
  23651.             pass
  23652.         
  23653.         return _.game.s.foundations[i].cards[-1].rank == cards[0].rank
  23654.  
  23655.  
  23656.  
  23657. class HexAKlon_RowStack(AC_RowStack):
  23658.     
  23659.     def acceptsCards(_, from_stack, cards):
  23660.         if AC_RowStack.acceptsCards(_, from_stack, cards):
  23661.             return 1
  23662.         
  23663.         stackcards = _.cards
  23664.         if not not stackcards and stackcards[-1].suit == 4:
  23665.             pass
  23666.         return cards[0].suit == 4
  23667.  
  23668.  
  23669.  
  23670. class BitsNBytes(Game):
  23671.     GAME_VERSION = 2
  23672.     
  23673.     def createGame(_):
  23674.         (l, s) = (Layout(_), _.s)
  23675.         font = getFont('canvas_card', cardw = l.CW)
  23676.         _.setSize(l.XM * 4 + l.XS * 8, l.YM + l.YS * 4)
  23677.         y = l.YM
  23678.         for j in range(4):
  23679.             x = l.XM * 4 + l.XS * 7
  23680.             for i in range(4):
  23681.                 s.rows.append(Bits_RowStack(x, y, _, max_cards = 1, max_accept = 1, base_suit = j, max_move = 0))
  23682.                 x = x - l.XS
  23683.             
  23684.             y = y + l.YS
  23685.         
  23686.         y = l.YM
  23687.         for j in range(4):
  23688.             x = l.XM * 3 + l.XS * 3
  23689.             for i in range(2):
  23690.                 s.rows.append(Bytes_RowStack(x, y, _, max_cards = 1, max_accept = 1, base_suit = ANY_SUIT, max_move = 0))
  23691.                 x = x - l.XS
  23692.             
  23693.             y = y + l.YS
  23694.         
  23695.         x = l.XM * 2 + l.XS
  23696.         y = l.YM
  23697.         for i in range(4):
  23698.             s.foundations.append(SS_FoundationStack(x, y, _, i, mod = 1, max_move = 0, max_cards = 1))
  23699.             y = y + l.YS
  23700.         
  23701.         _.setRegion(s.rows, (0, 0, 999999, 999999))
  23702.         x = l.XM
  23703.         y = l.YM
  23704.         s.talon = WasteTalonStack(x, y, _, num_deal = 2, max_rounds = 2)
  23705.         l.createText(s.talon, 'ss')
  23706.         y = y + l.YS + l.YM * 2
  23707.         s.waste = WasteStack(x, y, _)
  23708.         l.createText(s.waste, 'ss')
  23709.         l.defaultStackGroups()
  23710.  
  23711.     
  23712.     def _shuffleHook(_, cards):
  23713.         (topcards, ranks) = ([
  23714.             None] * 4, [
  23715.             None] * 4)
  23716.         for c in cards[:]:
  23717.             pass
  23718.         
  23719.         cards = topcards + cards
  23720.         cards.reverse()
  23721.         return cards
  23722.  
  23723.     
  23724.     def startGame(_):
  23725.         if not __debug__ and len(_.s.talon.cards) == 68:
  23726.             raise AssertionError
  23727.         _.startDealSample()
  23728.         _.s.talon.dealRow(rows = _.s.foundations)
  23729.         _.s.talon.dealCards()
  23730.  
  23731.     
  23732.     def isGameWon(_):
  23733.         for s in _.s.rows:
  23734.             pass
  23735.         
  23736.         return 1
  23737.  
  23738.     
  23739.     def shallHighlightMatch(_, stack1, card1, stack2, card2):
  23740.         return 0
  23741.  
  23742.  
  23743.  
  23744. class HexAKlon(Game):
  23745.     Hint_Class = CautiousDefaultHint
  23746.     
  23747.     def createGame(_):
  23748.         (l, s) = (Layout(_), _.s)
  23749.         font = getFont('canvas_card', cardw = l.CW)
  23750.         _.setSize(l.XM + l.XS * 8, l.YM + l.YS * 5)
  23751.         x = l.XM
  23752.         y = l.YM
  23753.         s.talon = WasteTalonStack(x, y, _, num_deal = 1, max_rounds = -1)
  23754.         l.createText(s.talon, 'ss')
  23755.         x = x + l.XS
  23756.         s.waste = WasteStack(x, y, _)
  23757.         l.createText(s.waste, 'ss')
  23758.         x = l.XM + l.XS * 3
  23759.         for i in range(4):
  23760.             s.foundations.append(SS_FoundationStack(x, y, _, i, mod = 16, max_move = 16, max_cards = 16))
  23761.             x = x + l.XS
  23762.         
  23763.         s.foundations.append(HexATrump_Foundation(x, y, _, 4, mod = 4, max_move = 0, max_cards = 4))
  23764.         x = l.XM
  23765.         y = l.YM * 3 + l.YS
  23766.         for i in range(8):
  23767.             s.rows.append(HexAKlon_RowStack(x, y, _, max_cards = 99, max_accept = 99, base_suit = ANY_SUIT, base_rank = 15, max_move = 99))
  23768.             x = x + l.XS
  23769.         
  23770.         _.setRegion(s.rows, (0, y - l.YM / 2, 999999, 999999))
  23771.         l.defaultStackGroups()
  23772.  
  23773.     
  23774.     def startGame(_):
  23775.         if not __debug__ and len(_.s.talon.cards) == 68:
  23776.             raise AssertionError
  23777.         for i in range(len(_.s.rows)):
  23778.             _.s.talon.dealRow(rows = _.s.rows[i + 1:], flip = 0, frames = 0)
  23779.         
  23780.         _.startDealSample()
  23781.         _.s.talon.dealRow()
  23782.         _.s.talon.dealCards()
  23783.  
  23784.     
  23785.     def shallHighlightMatch(_, stack1, card1, stack2, card2):
  23786.         return 0
  23787.  
  23788.  
  23789. registerGame(GameInfo(165, BitsNBytes, 'Bits n Bytes', GI.GT_HEXADECK, 1, 1, suits = range(4), ranks = range(16), trumps = range(4)))
  23790. registerGame(GameInfo(166, HexAKlon, 'Hex A Klon', GI.GT_HEXADECK, 1, -1, suits = range(4), ranks = range(16), trumps = range(4)))
  23791.  
  23792. class Memory_RowStack(OpenStack):
  23793.     
  23794.     def clickHandler(_, event):
  23795.         game = _.game
  23796.         if len(_.cards) != 1 or _.cards[-1].face_up:
  23797.             return 1
  23798.         
  23799.         if game.other_stack is None:
  23800.             game.playSample('flip', priority = 5)
  23801.             _.flipMove()
  23802.             game.other_stack = _
  23803.         elif __debug__:
  23804.             if not len(game.other_stack.cards) == 1 and game.other_stack.cards[-1].face_up:
  23805.                 raise AssertionError
  23806.         (c1, c2) = (_.cards[-1], game.other_stack.cards[0])
  23807.         _.flipMove()
  23808.         if _.game.cardsMatch(c1, c2):
  23809.             _._dropPairMove(1, game.other_stack)
  23810.         else:
  23811.             game.playSample('flip', priority = 5)
  23812.             game.score = game.score - 1
  23813.             game.updateStatus(moves = game.moves.index + 1)
  23814.             game.updateText()
  23815.             game.canvas.update_idletasks()
  23816.             game.sleep(0.5)
  23817.             game.other_stack.flipMove()
  23818.             game.canvas.update_idletasks()
  23819.             game.sleep(0.2)
  23820.             _.flipMove()
  23821.         game.other_stack = None
  23822.         _.game.finishMove()
  23823.         return 1
  23824.  
  23825.     
  23826.     def _dropPairMove(_, n, other_stack, frames = -1, shadow = -1):
  23827.         game = _.game
  23828.         game.playSample('droppair', priority = 200)
  23829.         game.closed_cards = game.closed_cards - 2
  23830.         game.score = game.score + 5
  23831.  
  23832.     rightclickHandler = clickHandler
  23833.     doubleclickHandler = clickHandler
  23834.     
  23835.     def controlclickHandler(_, event):
  23836.         return 0
  23837.  
  23838.     
  23839.     def shiftclickHandler(_, event):
  23840.         return 0
  23841.  
  23842.  
  23843.  
  23844. class Memory24(Game):
  23845.     Hint_Class = None
  23846.     COLUMNS = 6
  23847.     ROWS = 4
  23848.     WIN_SCORE = 40
  23849.     PERFECT_SCORE = 60
  23850.     
  23851.     def createGame(_):
  23852.         (l, s) = (Layout(_), _.s)
  23853.         _.other_stack = None
  23854.         _.closed_cards = -1
  23855.         _.score = 0
  23856.         (x, y) = (l.XM, _.ROWS * l.YS)
  23857.         if _.preview <= 1:
  23858.             _.texts.score = MfxCanvasText(_.canvas, x, y, anchor = 'sw', font = getFont('canvas_large'))
  23859.             x = _.texts.score.bbox()[2] + 16
  23860.         
  23861.         w = max(2 * l.XS, x)
  23862.         _.setSize(l.XM + w + _.COLUMNS * l.XS, l.YM + _.ROWS * l.YS)
  23863.         for i in range(_.ROWS):
  23864.             for j in range(_.COLUMNS):
  23865.                 (x, y) = (l.XM + w + j * l.XS, l.YM + i * l.YS)
  23866.                 s.rows.append(Memory_RowStack(x, y, _, max_move = 0, max_accept = 0, max_cards = 1))
  23867.             
  23868.         
  23869.         (x, y) = (l.XM, l.YM)
  23870.         s.talon = InitialDealTalonStack(x, y, _)
  23871.         l.createText(s.talon, anchor = 'nn', text_format = '%D')
  23872.         s.internals.append(InvisibleStack(_))
  23873.         l.defaultStackGroups()
  23874.  
  23875.     
  23876.     def startGame(_):
  23877.         n = _.COLUMNS * _.ROWS
  23878.         if not __debug__ and len(_.s.talon.cards) == n:
  23879.             raise AssertionError
  23880.         _.other_stack = None
  23881.         _.closed_cards = n
  23882.         _.score = 0
  23883.         _.updateText()
  23884.         n = n - _.COLUMNS
  23885.         _.s.talon.dealRow(rows = _.s.rows[:n], flip = 0, frames = 0)
  23886.         _.startDealSample()
  23887.         _.s.talon.dealRow(rows = _.s.rows[n:], flip = 0)
  23888.         if not __debug__ and len(_.s.talon.cards) == 0:
  23889.             raise AssertionError
  23890.  
  23891.     
  23892.     def isGameWon(_):
  23893.         if _.closed_cards == 0:
  23894.             pass
  23895.         return _.score >= _.WIN_SCORE
  23896.  
  23897.     
  23898.     def getAutoStacks(_, event = None):
  23899.         return ((), (), ())
  23900.  
  23901.     
  23902.     def updateText(_):
  23903.         if _.preview > 1 or not (_.texts.score):
  23904.             return None
  23905.         
  23906.         t = ''
  23907.         if _.closed_cards:
  23908.             t = 'Points: %d' % _.score
  23909.         elif _.score >= _.WIN_SCORE:
  23910.             t = 'WON\n\n'
  23911.         
  23912.         t = t + 'Total: %d' % _.score
  23913.         _.texts.score.config(text = t)
  23914.  
  23915.     
  23916.     def getGameScore(_):
  23917.         return _.score
  23918.  
  23919.     
  23920.     def getWinStatus(_):
  23921.         (won, status, updated) = Game.getWinStatus(_)
  23922.         if status == 2 and _.score < _.PERFECT_SCORE:
  23923.             return (won, 1, _.U_WON)
  23924.         
  23925.         return (won, status, updated)
  23926.  
  23927.     
  23928.     def cardsMatch(_, card1, card2):
  23929.         if card1.suit == card2.suit:
  23930.             pass
  23931.         return card1.rank == card2.rank
  23932.  
  23933.     
  23934.     def canSaveGame(_):
  23935.         return 0
  23936.  
  23937.     
  23938.     def canUndo(_):
  23939.         return 0
  23940.  
  23941.     
  23942.     def _restoreGameHook(_, game):
  23943.         if game.loadinfo.other_stack_id >= 0:
  23944.             _.other_stack = _.allstacks[game.loadinfo.other_stack_id]
  23945.         else:
  23946.             _.other_stack = None
  23947.         _.closed_cards = game.loadinfo.closed_cards
  23948.         _.score = game.loadinfo.score
  23949.  
  23950.     
  23951.     def _loadGameHook(_, p):
  23952.         _.loadinfo.addattr(other_stack_id = p.load())
  23953.         _.loadinfo.addattr(closed_cards = p.load())
  23954.         _.loadinfo.addattr(score = p.load())
  23955.  
  23956.     
  23957.     def _saveGameHook(_, p):
  23958.         if _.other_stack:
  23959.             p.dump(_.other_stack.id)
  23960.         else:
  23961.             p.dump(-1)
  23962.         p.dump(_.closed_cards)
  23963.         p.dump(_.score)
  23964.  
  23965.  
  23966.  
  23967. class Memory30(Memory24):
  23968.     COLUMNS = 6
  23969.     ROWS = 5
  23970.     WIN_SCORE = 45
  23971.     PERFECT_SCORE = 75
  23972.  
  23973.  
  23974. class Memory40(Memory24):
  23975.     COLUMNS = 8
  23976.     ROWS = 5
  23977.     WIN_SCORE = 50
  23978.     PERFECT_SCORE = 100
  23979.  
  23980.  
  23981. class Concentration_RowStack(Memory_RowStack):
  23982.     
  23983.     def _dropPairMove(_, n, other_stack, frames = -1, shadow = -1):
  23984.         game = _.game
  23985.         game.playSample('droppair', priority = 200)
  23986.         game.closed_cards = game.closed_cards - 2
  23987.         game.score = game.score + 5
  23988.         old_state = game.enterState(game.S_FILL)
  23989.         f = game.s.talon
  23990.         game.moveMove(n, _, f, frames = frames, shadow = shadow)
  23991.         game.moveMove(n, other_stack, f, frames = frames, shadow = shadow)
  23992.         game.leaveState(old_state)
  23993.  
  23994.  
  23995.  
  23996. class Concentration(Memory24):
  23997.     COLUMNS = 13
  23998.     ROWS = 4
  23999.     WIN_SCORE = 50
  24000.     PERFECT_SCORE = 130
  24001.     
  24002.     def createGame(_):
  24003.         (l, s) = (Layout(_, XM = 4), _.s)
  24004.         _.other_stack = None
  24005.         _.closed_cards = -1
  24006.         _.score = 0
  24007.         _.setSize(l.XM + _.COLUMNS * l.XS, l.YM + (_.ROWS + 1) * l.YS)
  24008.         for i in range(_.ROWS):
  24009.             for j in range(_.COLUMNS):
  24010.                 (x, y) = (l.XM + j * l.XS, l.YM + i * l.YS)
  24011.                 s.rows.append(Concentration_RowStack(x, y, _, max_move = 0, max_accept = 0, max_cards = 1))
  24012.             
  24013.         
  24014.         (x, y) = (l.XM + _.COLUMNS * l.XS / 2, _.height - l.YS)
  24015.         s.talon = InitialDealTalonStack(x, y, _)
  24016.         l.createText(s.talon, dx = -10, anchor = 'sw', text_format = '%D')
  24017.         (x, y) = (l.XM, _.height - l.YM)
  24018.         l.defaultStackGroups()
  24019.  
  24020.     
  24021.     def cardsMatch(_, card1, card2):
  24022.         return card1.rank == card2.rank
  24023.  
  24024.  
  24025. registerGame(GameInfo(176, Memory24, 'Memory 24', GI.GT_MEMORY | GI.GT_SCORE, 2, 0, suits = (0, 2), ranks = (0, 8, 9, 10, 11, 12)))
  24026. registerGame(GameInfo(219, Memory30, 'Memory 30', GI.GT_MEMORY | GI.GT_SCORE, 2, 0, suits = (0, 2, 3), ranks = (0, 9, 10, 11, 12)))
  24027. registerGame(GameInfo(177, Memory40, 'Memory 40', GI.GT_MEMORY | GI.GT_SCORE, 2, 0, suits = (0, 2), ranks = (0, 4, 5, 6, 7, 8, 9, 10, 11, 12)))
  24028. registerGame(GameInfo(178, Concentration, 'Concentration', GI.GT_MEMORY | GI.GT_SCORE, 1, 0))
  24029.  
  24030. class Pegged_Hint(AbstractHint):
  24031.     
  24032.     def computeHints(_):
  24033.         game = _.game
  24034.         stacks = filter((lambda r: not (r.cards)), game.s.rows)
  24035.         for t in stacks:
  24036.             for dx, dy in game.STEPS:
  24037.                 r = game.map.get((t.pos[0] + dx, t.pos[1] + dy))
  24038.                 score = 10000 + game.app.miscrandom.randint(0, 9999)
  24039.                 _.addHint(score, 1, r, t)
  24040.             
  24041.         
  24042.  
  24043.  
  24044.  
  24045. class Pegged_RowStack(ReserveStack):
  24046.     
  24047.     def acceptsCards(_, from_stack, cards):
  24048.         if not ReserveStack.acceptsCards(_, from_stack, cards):
  24049.             return 0
  24050.         
  24051.         return _._getMiddleStack(from_stack) is not None
  24052.  
  24053.     
  24054.     def canDropCards(_, stacks):
  24055.         return (None, 0)
  24056.  
  24057.     
  24058.     def moveMove(_, ncards, to_stack, frames = -1, shadow = -1):
  24059.         other_stack = to_stack._getMiddleStack(_)
  24060.         old_state = _.game.enterState(_.game.S_FILL)
  24061.         f = _.game.s.foundations[0]
  24062.         _.game.moveMove(ncards, _, to_stack, frames = 0)
  24063.         _.game.playSample('drop', priority = 200)
  24064.         _.game.moveMove(ncards, other_stack, f, frames = -1, shadow = shadow)
  24065.         _.game.leaveState(old_state)
  24066.         _.fillStack()
  24067.         other_stack.fillStack()
  24068.  
  24069.     
  24070.     def _getMiddleStack(_, from_stack):
  24071.         (dx, dy) = (from_stack.pos[0] - _.pos[0], from_stack.pos[1] - _.pos[1])
  24072.         if not _.game.STEP_MAP.get((dx, dy)):
  24073.             return None
  24074.         
  24075.         s = _.game.map.get((_.pos[0] + dx / 2, _.pos[1] + dy / 2))
  24076.         if not s or not (s.cards):
  24077.             return None
  24078.         
  24079.         return s
  24080.  
  24081.     
  24082.     def copyModel(_, clone):
  24083.         ReserveStack.copyModel(_, clone)
  24084.         clone.pos = _.pos
  24085.  
  24086.  
  24087.  
  24088. class Pegged(Game):
  24089.     Hint_Class = Pegged_Hint
  24090.     STEPS = ((-4, 0), (4, 0), (0, -4), (0, 4))
  24091.     ROWS = (3, 5, 7, 7, 7, 5, 3)
  24092.     EMPTY_STACK_ID = -1
  24093.     
  24094.     def createGame(_):
  24095.         (l, s) = (Layout(_), _.s)
  24096.         n = m = max(_.ROWS)
  24097.         if _.ROWS[0] == m or _.ROWS[-1] == m:
  24098.             n = n + 1
  24099.         
  24100.         _.setSize(l.XM + n * l.XS, l.YM + len(_.ROWS) * l.YS)
  24101.         _.map = { }
  24102.         for i in range(len(_.ROWS)):
  24103.             r = _.ROWS[i]
  24104.             for j in range(r):
  24105.                 d = (m - r) + 2 * j
  24106.                 (x, y) = (l.XM + d * l.XS / 2, l.YM + i * l.YS)
  24107.                 stack = Pegged_RowStack(x, y, _)
  24108.                 stack.pos = (d, 2 * i)
  24109.                 s.rows.append(stack)
  24110.                 _.map[stack.pos] = stack
  24111.             
  24112.         
  24113.         (x, y) = (_.width - l.XS, l.YM)
  24114.         s.foundations.append(AbstractFoundationStack(x, y, _, ANY_SUIT, max_move = 0, max_accept = 0))
  24115.         l.createText(s.foundations[0], 'ss')
  24116.         y = _.height - l.YS
  24117.         s.talon = InitialDealTalonStack(x, y, _)
  24118.         s.internals.append(InvisibleStack(_))
  24119.         _.STEP_MAP = { }
  24120.         for step in _.STEPS:
  24121.             _.STEP_MAP[step] = 1
  24122.         
  24123.         l.defaultStackGroups()
  24124.  
  24125.     
  24126.     def startGame(_):
  24127.         n = (len(_.cards) - len(_.s.rows)) + 1
  24128.         if n > 0:
  24129.             _.moveMove(n, _.s.talon, _.s.internals[0], frames = 0)
  24130.         
  24131.         _.startDealSample()
  24132.         rows = list(_.s.rows[:])
  24133.         rows.remove(rows[_.EMPTY_STACK_ID])
  24134.         _.s.talon.dealRow(rows = rows, frames = 4)
  24135.         if not __debug__ and len(_.s.talon.cards) == 0:
  24136.             raise AssertionError
  24137.  
  24138.     
  24139.     def isGameWon(_):
  24140.         c = 0
  24141.         for s in _.s.foundations:
  24142.             c = c + len(s.cards)
  24143.         
  24144.         return c + 1 == len(_.cards)
  24145.  
  24146.     
  24147.     def getAutoStacks(_, event = None):
  24148.         return ((), (), ())
  24149.  
  24150.     
  24151.     def getWinStatus(_):
  24152.         (won, status, updated) = Game.getWinStatus(_)
  24153.         if status == 2:
  24154.             stacks = filter((lambda r: r.cards), game.s.rows)
  24155.             if not __debug__ and len(stacks) == 1:
  24156.                 raise AssertionError
  24157.             if stacks[0].id != _.EMPTY_STACK_ID:
  24158.                 return (won, 1, _.U_WON)
  24159.             
  24160.         
  24161.         return (won, status, updated)
  24162.  
  24163.     
  24164.     def getHighlightPilesStacks(_):
  24165.         rows = []
  24166.         for r in _.s.rows:
  24167.             (rx, ry) = r.pos
  24168.             for dx, dy in _.STEPS:
  24169.                 s = _.map.get((rx + dx, ry + dy))
  24170.             
  24171.         
  24172.         return ((rows, 1),)
  24173.  
  24174.  
  24175.  
  24176. class PeggedCross1(Pegged):
  24177.     ROWS = (3, 3, 7, 7, 7, 3, 3)
  24178.  
  24179.  
  24180. class PeggedCross2(Pegged):
  24181.     ROWS = (3, 3, 3, 9, 9, 9, 3, 3, 3)
  24182.  
  24183.  
  24184. class Pegged6x6(Pegged):
  24185.     EMPTY_STACK_ID = 14
  24186.     ROWS = (6, 6, 6, 6, 6, 6)
  24187.  
  24188.  
  24189. class Pegged7x7(Pegged):
  24190.     ROWS = (7, 7, 7, 7, 7, 7, 7)
  24191.  
  24192.  
  24193. class PeggedTriangle1(Pegged):
  24194.     STEPS = ((-2, -4), (-2, 4), (0, -4), (0, 4), (2, -4), (2, 4))
  24195.     ROWS = (1, 2, 3, 4, 5)
  24196.     EMPTY_STACK_ID = 4
  24197.  
  24198.  
  24199. class PeggedTriangle2(PeggedTriangle1):
  24200.     ROWS = (1, 2, 3, 4, 5, 6)
  24201.  
  24202.  
  24203. def r(id, gameclass, name):
  24204.     si_ncards = 0
  24205.     for n in gameclass.ROWS:
  24206.         si_ncards = si_ncards + n
  24207.     
  24208.     gi = GameInfo(id, gameclass, name, GI.GT_PUZZLE_TYPE, 1, 0, si = {
  24209.         'ncards': si_ncards }, rules_filename = 'pegged.html')
  24210.     registerGame(gi)
  24211.     return gi
  24212.  
  24213. r(180, Pegged, 'Pegged')
  24214. r(181, PeggedCross1, 'Pegged Cross 1')
  24215. r(182, PeggedCross2, 'Pegged Cross 2')
  24216. r(183, Pegged6x6, 'Pegged 6x6')
  24217. r(184, Pegged7x7, 'Pegged 7x7')
  24218. r(210, PeggedTriangle1, 'Pegged Triangle 1')
  24219. r(211, PeggedTriangle2, 'Pegged Triangle 2')
  24220. del r
  24221.  
  24222. class Wicked_Talon(Cruel_Talon):
  24223.     pass
  24224.  
  24225.  
  24226. class ImperialTrump_Foundation(SS_FoundationStack):
  24227.     
  24228.     def acceptsCards(_, from_stack, cards):
  24229.         if not SS_FoundationStack.acceptsCards(_, from_stack, cards):
  24230.             return 0
  24231.         
  24232.         return cards[-1].rank < len(_.game.s.foundations[4].cards)
  24233.  
  24234.  
  24235.  
  24236. class Ponytail_Foundation(Braid_Foundation):
  24237.     pass
  24238.  
  24239.  
  24240. class Tarock_OpenStack(OpenStack):
  24241.     
  24242.     def __init__(_, x, y, game, yoffset = -1, **cap):
  24243.         kwdefault(cap, max_move = 999999, max_accept = 999999)
  24244.         apply(OpenStack.__init__, (_, x, y, game), cap)
  24245.         if yoffset < 0:
  24246.             yoffset = game.app.images.CARD_YOFFSET
  24247.         
  24248.         _.CARD_YOFFSET = yoffset
  24249.  
  24250.  
  24251.  
  24252. class Tarock_AC_RowStack(Tarock_OpenStack):
  24253.     
  24254.     def acceptsCards(_, from_stack, cards):
  24255.         if not _.basicAcceptsCards(from_stack, cards):
  24256.             return 0
  24257.         
  24258.         if not (_.cards):
  24259.             return 1
  24260.         
  24261.         if cards[0].rank != _.cards[-1].rank - 1:
  24262.             return 0
  24263.         elif cards[0].color == 2 or _.cards[-1].color == 2:
  24264.             return 1
  24265.         else:
  24266.             return cards[0].color != _.cards[-1].color
  24267.  
  24268.  
  24269.  
  24270. class Skiz_RowStack(RK_RowStack):
  24271.     
  24272.     def acceptsCards(_, from_stack, cards):
  24273.         if not _.basicAcceptsCards(from_stack, cards):
  24274.             return 0
  24275.         
  24276.         if not (_.cards):
  24277.             if cards[0].suit == len(_.game.gameinfo.suits):
  24278.                 return cards[0].rank == len(_.game.gameinfo.trumps) - 1
  24279.             else:
  24280.                 return cards[0].rank == len(_.game.gameinfo.ranks) - 1
  24281.         
  24282.         if _.cards[-1].suit == cards[0].suit:
  24283.             pass
  24284.         return _.cards[-1].rank - 1 == cards[0].rank
  24285.  
  24286.  
  24287.  
  24288. class Pagat_RowStack(RK_RowStack):
  24289.     
  24290.     def acceptsCards(_, from_stack, cards):
  24291.         if not _.basicAcceptsCards(from_stack, cards):
  24292.             return 0
  24293.         
  24294.         if not (_.cards):
  24295.             return 1
  24296.         
  24297.         if _.cards[-1].suit == cards[0].suit:
  24298.             pass
  24299.         return _.cards[-1].rank - 1 == cards[0].rank
  24300.  
  24301.  
  24302.  
  24303. class TrumpWild_RowStack(Tarock_OpenStack):
  24304.     
  24305.     def acceptsCards(_, from_stack, cards):
  24306.         if not _.basicAcceptsCards(from_stack, cards):
  24307.             return 0
  24308.         
  24309.         if not (_.cards):
  24310.             if cards[0].suit == len(_.game.gameinfo.suits):
  24311.                 return cards[0].rank == len(_.game.gameinfo.trumps) - 1
  24312.             else:
  24313.                 return cards[0].rank == len(_.game.gameinfo.ranks) - 1
  24314.         
  24315.         if cards[0].rank != _.cards[-1].rank - 1:
  24316.             return 0
  24317.         elif cards[0].color == 2 or _.cards[-1].color == 2:
  24318.             return 1
  24319.         else:
  24320.             return cards[0].color != _.cards[-1].color
  24321.  
  24322.  
  24323.  
  24324. class TrumpOnly_RowStack(Tarock_OpenStack):
  24325.     
  24326.     def acceptsCards(_, from_stack, cards):
  24327.         if not _.basicAcceptsCards(from_stack, cards):
  24328.             return 0
  24329.         
  24330.         if not (_.cards):
  24331.             return cards[0].suit == len(_.game.gameinfo.suits)
  24332.         
  24333.         if cards[0].color == 2:
  24334.             pass
  24335.         return cards[0].rank == _.cards[-1].rank - 1
  24336.  
  24337.     
  24338.     def getBottomImage(_):
  24339.         return _.game.app.images.getReserveBottom()
  24340.  
  24341.  
  24342.  
  24343. class Excuse_RowStack(Tarock_OpenStack):
  24344.     
  24345.     def acceptsCards(_, from_stack, cards):
  24346.         if not _.basicAcceptsCards(from_stack, cards):
  24347.             return 0
  24348.         
  24349.         if not (_.cards):
  24350.             return 0
  24351.         
  24352.         return cards[0].rank == _.cards[-1].rank - 1
  24353.  
  24354.  
  24355.  
  24356. class WheelOfFortune_RowStack(Tarock_OpenStack):
  24357.     
  24358.     def acceptsCards(_, from_stack, cards):
  24359.         if not _.basicAcceptsCards(from_stack, cards):
  24360.             return 0
  24361.         
  24362.         if not (_.cards):
  24363.             return 1
  24364.         
  24365.         if cards[0].suit == _.cards[-1].suit:
  24366.             pass
  24367.         return cards[0].rank == _.cards[-1].rank - 1
  24368.  
  24369.     
  24370.     def getBottomImage(_):
  24371.         return _.game.app.images.getReserveBottom()
  24372.  
  24373.  
  24374.  
  24375. class Ponytail_PonytailStack(Braid_BraidStack):
  24376.     pass
  24377.  
  24378.  
  24379. class Ponytail_RowStack(Braid_RowStack):
  24380.     pass
  24381.  
  24382.  
  24383. class Ponytail_ReserveStack(Braid_ReserveStack):
  24384.     pass
  24385.  
  24386.  
  24387. class Cavalier_RowStack(Tarock_AC_RowStack):
  24388.     
  24389.     def acceptsCards(_, from_stack, cards):
  24390.         if not Tarock_AC_RowStack.acceptsCards(_, from_stack, cards):
  24391.             return 0
  24392.         
  24393.         if not _.cards:
  24394.             pass
  24395.         return len(cards) == 1
  24396.  
  24397.     
  24398.     def canMoveCards(_, cards):
  24399.         for i in range(len(cards) - 1):
  24400.             if not (cards[i].suit == 4):
  24401.                 if cards[i].color == cards[i + 1].color:
  24402.                     return 0
  24403.                 
  24404.             
  24405.             if cards[i].rank - 1 != cards[i + 1].rank:
  24406.                 return 0
  24407.             
  24408.         
  24409.         return 1
  24410.  
  24411.  
  24412.  
  24413. class Nasty_RowStack(SS_RowStack):
  24414.     
  24415.     def acceptsCards(_, from_stack, cards):
  24416.         if not _.basicAcceptsCards(from_stack, cards):
  24417.             return 0
  24418.         
  24419.         if _.cards:
  24420.             if cards[0].rank == _.cards[-1].rank - 1:
  24421.                 pass
  24422.             return cards[0].suit == _.cards[-1].suit
  24423.         
  24424.         return cards[0].rank == 13 + 8 * (cards[0].suit == 4)
  24425.  
  24426.  
  24427.  
  24428. class Tarock_GameMethods:
  24429.     SUITS = ('Wand', 'Sword', 'Cup', 'Coin', 'Trump')
  24430.     RANKS = ('Ace', '2', '3', '4', '5', '6', '7', '8', '9', '10', 'Page', 'Valet', 'Queen', 'King')
  24431.     
  24432.     def getCardFaceImage(_, deck, suit, rank):
  24433.         return _.app.images.getFace(deck, suit, rank)
  24434.  
  24435.  
  24436.  
  24437. class AbstractTarockGame(Tarock_GameMethods, Game):
  24438.     pass
  24439.  
  24440.  
  24441. class WheelOfFortune(AbstractTarockGame):
  24442.     Hint_Class = CautiousDefaultHint
  24443.     
  24444.     def createGame(_):
  24445.         (l, s) = (Layout(_), _.s)
  24446.         font = getFont('canvas_card', cardw = l.CW)
  24447.         _.setSize(l.XM + l.XS * 11.5, l.YM + l.YS * 5.5)
  24448.         xoffset = (1, 2, 3, 3.9, 3, 2, 1, 0, -1, -2, -3, -3.9, -3, -2, -1, 0, -2, -1, 0, 1, 2)
  24449.         yoffset = (0.2, 0.5, 1.1, 2.2, 3.3, 3.9, 4.2, 4.4, 4.2, 3.9, 3.3, 2.2, 1.1, 0.5, 0.2, 0, 1.8, 2.1, 2.2, 2.4, 2.6)
  24450.         x = l.XM + l.XS * 4
  24451.         y = l.YM
  24452.         for i in range(21):
  24453.             x0 = x + xoffset[i] * l.XS
  24454.             y0 = y + yoffset[i] * l.YS
  24455.             s.rows.append(WheelOfFortune_RowStack(x0, y0, _, yoffset = l.CH / 4, max_cards = 2, max_move = 1, max_accept = 1))
  24456.         
  24457.         _.setRegion(s.rows, (-999, -999, l.XS * 9, 999999))
  24458.         x = _.width - l.XS * 2
  24459.         y = l.YM
  24460.         s.foundations.append(SS_FoundationStack(x, y, _, 0, max_cards = 14))
  24461.         x = x + l.XS
  24462.         s.foundations.append(SS_FoundationStack(x, y, _, 1, max_cards = 14))
  24463.         y = y + l.YS
  24464.         s.foundations.append(SS_FoundationStack(x, y, _, 3, max_cards = 14))
  24465.         x = x - l.XS
  24466.         s.foundations.append(SS_FoundationStack(x, y, _, 2, max_cards = 14))
  24467.         x = x + l.XS * 0.5
  24468.         y = y + l.YS
  24469.         s.foundations.append(SS_FoundationStack(x, y, _, 4, max_cards = 22))
  24470.         x = _.width - l.XS
  24471.         y = _.height - l.YS * 1.5
  24472.         s.talon = WasteTalonStack(x, y, _, num_deal = 2, max_rounds = 1)
  24473.         l.createText(s.talon, 'nn')
  24474.         x = x - l.XS
  24475.         s.waste = WasteStack(x, y, _)
  24476.         l.createText(s.waste, 'nn')
  24477.         l.defaultStackGroups()
  24478.  
  24479.     
  24480.     def startGame(_):
  24481.         if not __debug__ and len(_.s.talon.cards) == 78:
  24482.             raise AssertionError
  24483.         _.startDealSample()
  24484.         _.s.talon.dealRow(rows = _.s.rows[-5:])
  24485.         _.s.talon.dealRow(rows = _.s.rows[4:-5])
  24486.         _.s.talon.dealRow(rows = _.s.rows[:4])
  24487.         _.s.talon.dealCards()
  24488.  
  24489.     
  24490.     def shallHighlightMatch(_, stack1, card1, stack2, card2):
  24491.         return 0
  24492.  
  24493.  
  24494.  
  24495. class ImperialTrumps(AbstractTarockGame):
  24496.     
  24497.     def createGame(_):
  24498.         (l, s) = (Layout(_), _.s)
  24499.         font = getFont('canvas_card', cardw = l.CW)
  24500.         _.setSize(l.XM + l.XS * 8, l.YM + l.YS * 5)
  24501.         x = l.XM + l.XS * 3
  24502.         y = l.YM
  24503.         for i in range(4):
  24504.             s.foundations.append(ImperialTrump_Foundation(x, y, _, i, max_cards = 14))
  24505.             x = x + l.XS
  24506.         
  24507.         s.foundations.append(SS_FoundationStack(x, y, _, 4, max_cards = 22))
  24508.         x = l.XM
  24509.         s.talon = WasteTalonStack(x, y, _, num_deal = 1, max_rounds = -1)
  24510.         l.createText(s.talon, 'ss')
  24511.         x = x + l.XS
  24512.         s.waste = WasteStack(x, y, _)
  24513.         l.createText(s.waste, 'ss')
  24514.         x = l.XM
  24515.         y = l.YM + int(round(l.YS * 1.25))
  24516.         for i in range(8):
  24517.             s.rows.append(TrumpWild_RowStack(x, y, _))
  24518.             x = x + l.XS
  24519.         
  24520.         _.setRegion(s.rows, (-999, y, 999999, 999999))
  24521.         l.defaultStackGroups()
  24522.  
  24523.     
  24524.     def startGame(_, reverse = 1):
  24525.         if not __debug__ and len(_.s.talon.cards) == 78:
  24526.             raise AssertionError
  24527.         for i in range(1, len(_.s.rows)):
  24528.             _.s.talon.dealRow(rows = _.s.rows[i:], flip = 0, frames = 0)
  24529.         
  24530.         _.startDealSample()
  24531.         _.s.talon.dealRow(reverse = reverse)
  24532.         _.s.talon.dealCards()
  24533.  
  24534.     
  24535.     def shallHighlightMatch(_, stack1, card1, stack2, card2):
  24536.         return 0
  24537.  
  24538.  
  24539.  
  24540. class Pagat(AbstractTarockGame):
  24541.     
  24542.     def createGame(_):
  24543.         (l, s) = (Layout(_), _.s)
  24544.         font = getFont('canvas_card', cardw = l.CW)
  24545.         h = max(3 * l.YS, 20 * l.YOFFSET)
  24546.         _.setSize(l.XM + 12 * l.XS, l.YM + l.YS + h)
  24547.         x = l.XM + l.XS * 3.5
  24548.         y = l.YM
  24549.         s.foundations.append(SS_FoundationStack(x, y, _, 0, max_cards = 14))
  24550.         x = x + l.XS
  24551.         s.foundations.append(SS_FoundationStack(x, y, _, 1, max_cards = 14))
  24552.         x = x + l.XS
  24553.         s.foundations.append(SS_FoundationStack(x, y, _, 4, max_cards = 22))
  24554.         x = x + l.XS
  24555.         s.foundations.append(SS_FoundationStack(x, y, _, 2, max_cards = 14))
  24556.         x = x + l.XS
  24557.         s.foundations.append(SS_FoundationStack(x, y, _, 3, max_cards = 14))
  24558.         x = l.XM
  24559.         for i in range(3):
  24560.             s.reserves.append(ReserveStack(x, y, _))
  24561.             x = x + l.XS
  24562.         
  24563.         x = x + l.XS * 6
  24564.         for i in range(3):
  24565.             s.reserves.append(ReserveStack(x, y, _))
  24566.             x = x + l.XS
  24567.         
  24568.         x = l.XM
  24569.         y = l.YM + l.YS * 1.1
  24570.         for i in range(12):
  24571.             s.rows.append(Pagat_RowStack(x, y, _))
  24572.             x = x + l.XS
  24573.         
  24574.         _.setRegion(s.rows, (-999, int(y), 999999, 999999))
  24575.         s.talon = InitialDealTalonStack(l.XM, _.height - l.YS, _)
  24576.         l.defaultStackGroups()
  24577.  
  24578.     
  24579.     def startGame(_):
  24580.         if not __debug__ and len(_.s.talon.cards) == 78:
  24581.             raise AssertionError
  24582.         for i in range(6):
  24583.             _.s.talon.dealRow(frames = 0)
  24584.         
  24585.         _.startDealSample()
  24586.         _.s.talon.dealRow(rows = _.s.rows[3:9])
  24587.  
  24588.     
  24589.     def shallHighlightMatch(_, stack1, card1, stack2, card2):
  24590.         if not card1.suit == card2.suit and card1.rank + 1 == card2.rank:
  24591.             pass
  24592.         return card2.rank + 1 == card1.rank
  24593.  
  24594.  
  24595.  
  24596. class Skiz(AbstractTarockGame):
  24597.     
  24598.     def createGame(_):
  24599.         (l, s) = (Layout(_), _.s)
  24600.         font = getFont('canvas_card', cardw = l.CW)
  24601.         h = max(3 * l.YS, 20 * l.YOFFSET)
  24602.         _.setSize(l.XM + 12 * l.XS, l.YM + l.YS + h)
  24603.         x = l.XM + l.XS * 3.5
  24604.         y = l.YM
  24605.         s.foundations.append(SS_FoundationStack(x, y, _, 0, max_cards = 14))
  24606.         x = x + l.XS
  24607.         s.foundations.append(SS_FoundationStack(x, y, _, 1, max_cards = 14))
  24608.         x = x + l.XS
  24609.         s.foundations.append(SS_FoundationStack(x, y, _, 4, max_cards = 22))
  24610.         x = x + l.XS
  24611.         s.foundations.append(SS_FoundationStack(x, y, _, 2, max_cards = 14))
  24612.         x = x + l.XS
  24613.         s.foundations.append(SS_FoundationStack(x, y, _, 3, max_cards = 14))
  24614.         x = l.XM
  24615.         for i in range(3):
  24616.             s.reserves.append(ReserveStack(x, y, _))
  24617.             x = x + l.XS
  24618.         
  24619.         x = x + l.XS * 6
  24620.         for i in range(3):
  24621.             s.reserves.append(ReserveStack(x, y, _))
  24622.             x = x + l.XS
  24623.         
  24624.         x = l.XM
  24625.         y = l.YM + l.YS * 1.1
  24626.         for i in range(12):
  24627.             s.rows.append(Skiz_RowStack(x, y, _))
  24628.             x = x + l.XS
  24629.         
  24630.         _.setRegion(s.rows, (-999, int(y), 999999, 999999))
  24631.         s.talon = InitialDealTalonStack(l.XM, _.height - l.YS, _)
  24632.         l.defaultStackGroups()
  24633.  
  24634.     
  24635.     def startGame(_):
  24636.         if not __debug__ and len(_.s.talon.cards) == 78:
  24637.             raise AssertionError
  24638.         for i in range(6):
  24639.             _.s.talon.dealRow(frames = 0)
  24640.         
  24641.         _.startDealSample()
  24642.         _.s.talon.dealRow(rows = _.s.rows[3:9])
  24643.  
  24644.     
  24645.     def shallHighlightMatch(_, stack1, card1, stack2, card2):
  24646.         if not card1.suit == card2.suit and card1.rank + 1 == card2.rank:
  24647.             pass
  24648.         return card2.rank + 1 == card1.rank
  24649.  
  24650.  
  24651.  
  24652. class FifteenPlus(AbstractTarockGame):
  24653.     
  24654.     def createGame(_):
  24655.         (l, s) = (Layout(_), _.s)
  24656.         font = getFont('canvas_card', cardw = l.CW)
  24657.         h = max(5 * l.YS, 20 * l.YOFFSET)
  24658.         _.setSize(l.XM + 9 * l.XS, l.YM + l.YS + h)
  24659.         x = _.width - l.XS
  24660.         y = l.YM
  24661.         s.foundations.append(SS_FoundationStack(x, y, _, 4, max_cards = 22))
  24662.         y = y + l.YS
  24663.         for i in range(4):
  24664.             s.foundations.append(SS_FoundationStack(x, y, _, i, max_cards = 14))
  24665.             y = y + l.YS
  24666.         
  24667.         x = l.XM
  24668.         y = l.YM
  24669.         for j in range(2):
  24670.             for i in range(8):
  24671.                 s.rows.append(Tarock_AC_RowStack(x, y, _, max_move = 1, max_accept = 1))
  24672.                 x = x + l.XS
  24673.             
  24674.             x = l.XM
  24675.             y = y + l.YS * 3
  24676.         
  24677.         _.setRegion(s.rows, (-999, -999, l.XM + l.XS * 8, 999999))
  24678.         s.talon = InitialDealTalonStack(l.XM, _.height - l.YS, _)
  24679.         l.defaultStackGroups()
  24680.  
  24681.     
  24682.     def startGame(_):
  24683.         if not __debug__ and len(_.s.talon.cards) == 78:
  24684.             raise AssertionError
  24685.         for i in range(2):
  24686.             _.s.talon.dealRow(flip = 0, frames = 0)
  24687.         
  24688.         for i in range(2):
  24689.             _.s.talon.dealRow(rows = _.s.rows[:15], flip = 0, frames = 0)
  24690.         
  24691.         _.startDealSample()
  24692.         _.s.talon.dealRow()
  24693.  
  24694.     
  24695.     def shallHighlightMatch(_, stack1, card1, stack2, card2):
  24696.         if not card1.suit == card2.suit and card1.rank + 1 == card2.rank:
  24697.             pass
  24698.         return card2.rank + 1 == card1.rank
  24699.  
  24700.  
  24701.  
  24702. class Excuse(AbstractTarockGame):
  24703.     GAME_VERSION = 2
  24704.     
  24705.     def createGame(_):
  24706.         (l, s) = (Layout(_), _.s)
  24707.         font = getFont('canvas_card', cardw = l.CW)
  24708.         h = max(5 * l.YS, 20 * l.YOFFSET)
  24709.         _.setSize(l.XM + 9 * l.XS, l.YM + l.YS + h)
  24710.         x = _.width - l.XS
  24711.         y = l.YM
  24712.         s.foundations.append(SS_FoundationStack(x, y, _, 4, max_cards = 22))
  24713.         y = y + l.YS
  24714.         for i in range(4):
  24715.             s.foundations.append(SS_FoundationStack(x, y, _, i, max_cards = 14))
  24716.             y = y + l.YS
  24717.         
  24718.         x = l.XM
  24719.         y = l.YM
  24720.         for j in range(2):
  24721.             for i in range(8):
  24722.                 s.rows.append(Excuse_RowStack(x, y, _, max_move = 1, max_accept = 1, base_rank = NO_RANK))
  24723.                 x = x + l.XS
  24724.             
  24725.             x = l.XM
  24726.             y = y + l.YS * 3
  24727.         
  24728.         _.setRegion(s.rows, (-999, -999, l.XM + l.XS * 8, 999999))
  24729.         s.talon = InitialDealTalonStack(l.XM, _.height - l.YS, _)
  24730.         l.defaultStackGroups()
  24731.  
  24732.     
  24733.     def _shuffleHook(_, cards):
  24734.         
  24735.         def isKing(c):
  24736.             if c.suit < 4 and c.rank == 13 and c.suit == 4:
  24737.                 pass
  24738.             return c.rank == 21
  24739.  
  24740.         (i, n) = (0, len(_.s.rows))
  24741.         kings = []
  24742.         for c in cards:
  24743.             i = i + 1
  24744.         
  24745.         for i in kings:
  24746.             j = i % n
  24747.             while j < i:
  24748.                 j = j + n
  24749.                 continue
  24750.                 None if isKing(c) else cards if not isKing(cards[j]) else kings
  24751.         
  24752.         cards.reverse()
  24753.         return cards
  24754.  
  24755.     
  24756.     def startGame(_):
  24757.         if not __debug__ and len(_.s.talon.cards) == 78:
  24758.             raise AssertionError
  24759.         for i in range(3):
  24760.             _.s.talon.dealRow(frames = 0)
  24761.         
  24762.         _.s.talon.dealRow(rows = _.s.rows[:15], frames = 0)
  24763.         _.startDealSample()
  24764.         _.s.talon.dealRow(rows = _.s.rows[:15])
  24765.  
  24766.     
  24767.     def shallHighlightMatch(_, stack1, card1, stack2, card2):
  24768.         if not card1.rank + 1 == card2.rank:
  24769.             pass
  24770.         return card1.rank - 1 == card2.rank
  24771.  
  24772.  
  24773.  
  24774. class Grasshopper(AbstractTarockGame):
  24775.     GAME_VERSION = 2
  24776.     
  24777.     def createGame(_):
  24778.         (l, s) = (Layout(_), _.s)
  24779.         font = getFont('canvas_card', cardw = l.CW)
  24780.         decks = _.gameinfo.decks
  24781.         _.setSize(2 * l.XM + (2 + 5 * decks) * l.XS, 3 * l.YM + 5 * l.YS)
  24782.         yoffset = min(l.YOFFSET, max(10, l.YOFFSET / 2))
  24783.         x = l.XM
  24784.         y = l.YM
  24785.         s.talon = WasteTalonStack(x, y, _, num_deal = 1, max_rounds = 2)
  24786.         l.createText(s.talon, 'ss')
  24787.         x = x + l.XS
  24788.         s.waste = WasteStack(x, y, _)
  24789.         l.createText(s.waste, 'ss')
  24790.         x = x + l.XM + l.XS
  24791.         for j in range(4):
  24792.             for i in range(decks):
  24793.                 s.foundations.append(SS_FoundationStack(x, y, _, j, max_cards = 14))
  24794.                 x = x + l.XS
  24795.             
  24796.         
  24797.         for i in range(decks):
  24798.             s.foundations.append(SS_FoundationStack(x, y, _, 4, max_cards = 22))
  24799.             x = x + l.XS
  24800.         
  24801.         x = l.XM
  24802.         y = l.YM * 3 + l.YS
  24803.         s.reserves.append(OpenStack(x, y, _))
  24804.         s.reserves[0].CARD_YOFFSET = (l.YOFFSET, yoffset)[decks == 2]
  24805.         x = x + l.XM + l.XS
  24806.         for i in range(decks):
  24807.             s.rows.append(TrumpOnly_RowStack(x, y, _, yoffset = yoffset))
  24808.             x = x + l.XS
  24809.         
  24810.         for i in range(4 * decks + 1):
  24811.             s.rows.append(Tarock_AC_RowStack(x, y, _))
  24812.             x = x + l.XS
  24813.         
  24814.         _.setRegion(s.rows, (-999, y - l.YS, 999999, 999999))
  24815.         l.defaultStackGroups()
  24816.  
  24817.     
  24818.     def startGame(_):
  24819.         decks = _.gameinfo.decks
  24820.         if not __debug__ and len(_.s.talon.cards) == 78 * decks:
  24821.             raise AssertionError
  24822.         _.startDealSample()
  24823.         for i in range(14 * decks):
  24824.             _.s.talon.dealRow(rows = _.s.reserves, flip = 0, frames = 4)
  24825.         
  24826.         _.s.reserves[0].flipMove()
  24827.         _.s.talon.dealRow(rows = _.s.rows[decks:])
  24828.         _.s.talon.dealCards()
  24829.  
  24830.     
  24831.     def fillStack(_, stack):
  24832.         r = _.s.reserves[0]
  24833.         if not (stack.cards) and stack in _.s.rows:
  24834.             if r.cards and stack.acceptsCards(r, r.cards[-1:]):
  24835.                 r.moveMove(1, stack)
  24836.             
  24837.         
  24838.         if r.canFlipCard():
  24839.             r.flipMove()
  24840.         
  24841.  
  24842.     
  24843.     def shallHighlightMatch(_, stack1, card1, stack2, card2):
  24844.         if card1.rank + 1 == card2.rank or card1.rank - 1 == card2.rank:
  24845.             pass
  24846.         return card1.color != card2.color
  24847.  
  24848.  
  24849.  
  24850. class DoubleGrasshopper(Grasshopper):
  24851.     pass
  24852.  
  24853.  
  24854. class Ponytail(Tarock_GameMethods, Braid):
  24855.     
  24856.     def createGame(_):
  24857.         (l, s) = (Layout(_), _.s)
  24858.         h = max(5 * l.YS + 30, l.YS + (_.BRAID_CARDS - 1) * l.YOFFSET)
  24859.         _.setSize(10 * l.XS + l.XM, l.YM + h)
  24860.         _.base_card = None
  24861.         s.addattr(braid = None)
  24862.         (x, y) = (l.XM, l.YM)
  24863.         for i in range(2):
  24864.             s.rows.append(Ponytail_RowStack(x + 0.5 * l.XS, y, _))
  24865.             s.rows.append(Ponytail_RowStack(x + 4.5 * l.XS, y, _))
  24866.             s.rows.append(Ponytail_RowStack(x + 5.5 * l.XS, y, _))
  24867.             s.rows.append(Ponytail_RowStack(x + 6.5 * l.XS, y, _))
  24868.             y = y + 4 * l.YS
  24869.         
  24870.         y = l.YM + l.YS
  24871.         for i in range(2):
  24872.             s.rows.append(Ponytail_ReserveStack(x, y, _))
  24873.             s.rows.append(Ponytail_ReserveStack(x + l.XS, y, _))
  24874.             s.rows.append(Ponytail_ReserveStack(x, y + l.YS, _))
  24875.             s.rows.append(Ponytail_ReserveStack(x + l.XS, y + l.YS, _))
  24876.             s.rows.append(Ponytail_ReserveStack(x, y + 2 * l.YS, _))
  24877.             s.rows.append(Ponytail_ReserveStack(x + l.XS, y + 2 * l.YS, _))
  24878.             x = x + 4 * l.XS
  24879.         
  24880.         x = l.XM + 5 * l.XS / 2
  24881.         y = l.YM
  24882.         s.braid = Ponytail_PonytailStack(x, y, _, sine = 1)
  24883.         x = l.XM + 7 * l.XS
  24884.         y = l.YM + 2 * l.YS
  24885.         s.talon = WasteTalonStack(x, y, _, max_rounds = 3)
  24886.         l.createText(s.talon, 'ss')
  24887.         s.talon.texts.rounds = MfxCanvasText(_.canvas, x + l.CW / 2, y - l.YM, anchor = 's')
  24888.         x = x - l.XS
  24889.         s.waste = WasteStack(x, y, _)
  24890.         l.createText(s.waste, 'ss')
  24891.         x = l.XM + 8 * l.XS
  24892.         y = l.YM
  24893.         for i in range(4):
  24894.             s.foundations.append(Ponytail_Foundation(x, y, _, i, mod = 14, max_cards = 14))
  24895.             s.foundations.append(Ponytail_Foundation(x + l.XS, y, _, i, mod = 14, max_cards = 14))
  24896.             y = y + l.YS
  24897.         
  24898.         s.foundations.append(Ponytail_Foundation(x, y, _, 4, mod = 22, max_cards = 22))
  24899.         s.foundations.append(Ponytail_Foundation(x + l.XS, y, _, 4, mod = 22, max_cards = 22))
  24900.         _.texts.info = MfxCanvasText(_.canvas, x + l.CW + l.XM / 2, y + l.YS, anchor = 'n', font = getFont('canvas_card', cardw = l.CW))
  24901.         _.sg.openstacks = s.foundations + s.rows
  24902.         _.sg.talonstacks = [
  24903.             s.talon] + [
  24904.             s.waste]
  24905.         _.sg.dropstacks = [
  24906.             s.braid] + s.rows + [
  24907.             s.waste]
  24908.  
  24909.  
  24910.  
  24911. class Cavalier(AbstractTarockGame):
  24912.     Layout_Method = Layout.bakersDozenLayout
  24913.     Talon_Class = InitialDealTalonStack
  24914.     Foundation_Class = SS_FoundationStack
  24915.     RowStack_Class = Cavalier_RowStack
  24916.     
  24917.     def createGame(_, **layout):
  24918.         (l, s) = (Layout(_), _.s)
  24919.         kwdefault(layout, rows = 18, playcards = 19)
  24920.         apply(_.Layout_Method, (l,), layout)
  24921.         _.setSize(l.size[0], l.size[1])
  24922.         for r in l.s.foundations:
  24923.             n = 14 + 8 * (r.suit == 4)
  24924.             s.foundations.append(_.Foundation_Class(r.x, r.y, _, r.suit, mod = n, max_cards = n))
  24925.         
  24926.         for r in l.s.rows:
  24927.             s.rows.append(_.RowStack_Class(r.x, r.y, _))
  24928.         
  24929.         s.talon = _.Talon_Class(l.s.talon.x, l.s.talon.y, _)
  24930.         l.defaultAll()
  24931.  
  24932.     
  24933.     def startGame(_, flip = (0, 1, 0), foundations = 0):
  24934.         if not __debug__ and len(_.s.talon.cards) == 78:
  24935.             raise AssertionError
  24936.         for f in flip:
  24937.             _.s.talon.dealRow(flip = f, frames = 0)
  24938.         
  24939.         _.startDealSample()
  24940.         _.s.talon.dealRow()
  24941.  
  24942.     
  24943.     def shallHighlightMatch(_, stack1, card1, stack2, card2):
  24944.         if card1.rank + 1 == card2.rank or card1.rank - 1 == card2.rank:
  24945.             if not card1.suit == 4 or card2.suit == 4:
  24946.                 pass
  24947.         return card1.color != card2.color
  24948.  
  24949.  
  24950.  
  24951. class FiveAces(Cavalier):
  24952.     
  24953.     def _shuffleHook(_, cards):
  24954.         return _._shuffleHookMoveToBottom(cards, (lambda c: (c.rank == 0, c.suit)))
  24955.  
  24956.     
  24957.     def startGame(_):
  24958.         Cavalier.startGame(_, foundations = 1)
  24959.  
  24960.  
  24961.  
  24962. class Wicked(FiveAces):
  24963.     Talon_Class = StackWrapper(Wicked_Talon, max_rounds = -1)
  24964.     RowStack_Class = StackWrapper(SS_RowStack, max_move = 1, max_accept = 1, base_rank = NO_RANK)
  24965.     Hint_Class = CautiousDefaultHint
  24966.     
  24967.     def startGame(_):
  24968.         Cavalier.startGame(_, flip = (1, 1, 1), foundations = 1)
  24969.  
  24970.     
  24971.     def shallHighlightMatch(_, stack1, card1, stack2, card2):
  24972.         if card1.rank + 1 == card2.rank or card1.rank - 1 == card2.rank:
  24973.             pass
  24974.         return card1.suit == card2.suit
  24975.  
  24976.  
  24977.  
  24978. class Nasty(Wicked):
  24979.     RowStack_Class = StackWrapper(Nasty_RowStack, max_move = 1, max_accept = 1, base_rank = ANY_RANK)
  24980.  
  24981.  
  24982. def r(id, gameclass, name, game_type, decks, redeals):
  24983.     game_type = game_type | GI.GT_TAROCK | GI.GT_CONTRIB | GI.GT_ORIGINAL
  24984.     gi = GameInfo(id, gameclass, name, game_type, decks, redeals, ranks = range(14), trumps = range(22))
  24985.     registerGame(gi)
  24986.     return gi
  24987.  
  24988. r(157, WheelOfFortune, 'Wheel of Fortune', GI.GT_TAROCK, 1, 0)
  24989. r(158, ImperialTrumps, 'Imperial Trumps', GI.GT_TAROCK, 1, -1)
  24990. r(159, Pagat, 'Pagat', GI.GT_TAROCK | GI.GT_OPEN, 1, 0)
  24991. r(160, Skiz, 'Skiz', GI.GT_TAROCK | GI.GT_OPEN, 1, 0)
  24992. r(161, FifteenPlus, 'Fifteen plus', GI.GT_TAROCK, 1, 0)
  24993. r(162, Excuse, 'Excuse', GI.GT_TAROCK | GI.GT_OPEN, 1, 0)
  24994. r(163, Grasshopper, 'Grasshopper', GI.GT_TAROCK, 1, 1)
  24995. r(164, DoubleGrasshopper, 'Double Grasshopper', GI.GT_TAROCK, 2, 1)
  24996. r(179, Ponytail, 'Ponytail', GI.GT_TAROCK, 2, 2)
  24997. r(202, Cavalier, 'Cavalier', GI.GT_TAROCK, 1, 0)
  24998. r(203, FiveAces, 'Five Aces', GI.GT_TAROCK, 1, 0)
  24999. r(204, Wicked, 'Wicked', GI.GT_TAROCK | GI.GT_OPEN, 1, -1)
  25000. r(205, Nasty, 'Nasty', GI.GT_TAROCK | GI.GT_OPEN, 1, -1)
  25001. del r
  25002.  
  25003. class Options:
  25004.     
  25005.     def __init__(_):
  25006.         _.version_tuple = VERSION_TUPLE
  25007.         _.saved = 0
  25008.         _.player = 'unknown'
  25009.         _.confirm = 1
  25010.         _.update_player_stats = 1
  25011.         _.autofaceup = 1
  25012.         _.autodrop = 0
  25013.         _.autodeal = 1
  25014.         _.quickplay = 1
  25015.         _.undo = 1
  25016.         _.bookmarks = 1
  25017.         _.hint = 1
  25018.         _.highlight_piles = 1
  25019.         _.highlight_cards = 1
  25020.         _.highlight_samerank = 1
  25021.         _.tablecolor = '#008200'
  25022.         _.animations = 2
  25023.         _.shadow = 0
  25024.         _.shade = 1
  25025.         _.hint_sleep = 1.5
  25026.         _.demo_sleep = 1.5
  25027.         _.demo_logo = 1
  25028.         _.demo_score = 0
  25029.         _.toolbar = 1
  25030.         _.toolbar_size = 0
  25031.         _.statusbar = 1
  25032.         _.sound = 1
  25033.         _.sound_mode = 1
  25034.         _.sound_sample_volume = 128
  25035.         _.sound_music_volume = 128
  25036.         _.recent_gameid = []
  25037.         _.last_gameid = 0
  25038.         _.last_player = None
  25039.         _.last_save_dir = None
  25040.         _.game_holded = 0
  25041.         _.wm_maximized = 0
  25042.         _.setDefaults()
  25043.         _.setConstants()
  25044.  
  25045.     
  25046.     def setDefaults(_, top = None):
  25047.         (sw, sh, sd) = (0, 0, 8)
  25048.         if top:
  25049.             (sw, sh, sd) = (top.winfo_screenwidth(), top.winfo_screenheight(), top.winfo_screendepth())
  25050.         
  25051.         if sd > 8:
  25052.             _.tabletile_name = 'Fade_Green.ppm'
  25053.         else:
  25054.             _.tabletile_name = None
  25055.         c = 'Standard'
  25056.         if sw < sw:
  25057.             pass
  25058.         elif not sw < 800:
  25059.             if sh < sh:
  25060.                 pass
  25061.             elif sh < 600:
  25062.                 c = '2000'
  25063.             
  25064.         _.cardset = {
  25065.             0: (c, ''),
  25066.             CSI.TYPE_FRENCH: (c, ''),
  25067.             CSI.TYPE_HANAFUDA: ('Kintengu', ''),
  25068.             CSI.TYPE_TAROCK: ('Vienna 2K', ''),
  25069.             CSI.TYPE_HEXADECK: ('Hex A Deck', ''),
  25070.             CSI.TYPE_MUGHAL_GANJIFA: ('Dashavatara Ganjifa', ''),
  25071.             CSI.TYPE_NAVAGRAHA_GANJIFA: ('Dashavatara Ganjifa', ''),
  25072.             CSI.TYPE_DASHAVATARA_GANJIFA: ('Dashavatara Ganjifa', '') }
  25073.  
  25074.     
  25075.     def setConstants(_):
  25076.         _.win_animation = 1
  25077.         _.toolbar_relief = 0
  25078.         _.dragcursor = 1
  25079.         _.magnetic_mouse = 0
  25080.         _.magnetic_mouse_time = 2.0
  25081.         _.raise_card_sleep = 1.0
  25082.         _.highlight_piles_sleep = 1.5
  25083.         _.highlight_piles_colors = (None, '#ffc000')
  25084.         _.highlight_cards_sleep = 1.5
  25085.         _.highlight_cards_colors = (None, '#ffc000', None, '#0000ff')
  25086.         _.highlight_samerank_sleep = 1.5
  25087.         _.highlight_samerank_colors = (None, '#ffc000', None, '#0000ff')
  25088.         _.hintarrow_color = '#303030'
  25089.         if PACKAGE == 'PyJongg':
  25090.             _.highlight_samerank = 0
  25091.             _.shadow = 0
  25092.             _.shade = 0
  25093.         
  25094.  
  25095.     
  25096.     def copy(_):
  25097.         opt = Options()
  25098.         merge_dict(opt.__dict__, _.__dict__)
  25099.         opt.setConstants()
  25100.         return opt
  25101.  
  25102.  
  25103.  
  25104. class Statistics:
  25105.     
  25106.     def __init__(_):
  25107.         _.version_tuple = VERSION_TUPLE
  25108.         _.saved = 0
  25109.         _.stats = { }
  25110.         _.demo_stats = { }
  25111.         _.prev_games = { }
  25112.         _.all_prev_games = { }
  25113.         _.session_games = { }
  25114.         _.total_balance = { }
  25115.         _.session_balance = { }
  25116.         _.gameid_balance = 0
  25117.  
  25118.     
  25119.     def new(_):
  25120.         return Statistics()
  25121.  
  25122.     
  25123.     def resetStats(_, player, gameid):
  25124.         _._Statistics__resetPrevGames(player, _.prev_games, gameid)
  25125.         _._Statistics__resetPrevGames(player, _.session_games, gameid)
  25126.         if not _.stats.has_key(player):
  25127.             return None
  25128.         
  25129.         if gameid == 0:
  25130.             del _.stats[player]
  25131.         else:
  25132.             (w0, l0) = _.getStats(player, 0)
  25133.             (w1, l1) = _.getStats(player, gameid)
  25134.             _.stats[player][0] = (w0 - w1, l0 - l1)
  25135.             _.stats[player][gameid] = (0, 0)
  25136.  
  25137.     
  25138.     def __resetPrevGames(_, player, games, gameid):
  25139.         if not games.has_key(player):
  25140.             return None
  25141.         
  25142.         if gameid == 0:
  25143.             del games[player]
  25144.         else:
  25145.             games[player] = filter((lambda a, b = gameid: a[0] != b), games[player])
  25146.  
  25147.     
  25148.     def getStats(_, player, gameid):
  25149.         d = _.stats.get(player)
  25150.         if d is None:
  25151.             d = _.stats[player] = { }
  25152.         
  25153.         return d.get(gameid, (0, 0))
  25154.  
  25155.     
  25156.     def updateStats(_, player, game, status):
  25157.         (won, lost) = (status > 0, status == 0)
  25158.         (w, l) = _.getStats(player, 0)
  25159.         _.stats[player][0] = (w + won, l + lost)
  25160.         (w, l) = _.getStats(player, game.id)
  25161.         _.stats[player][game.id] = (w + won, l + lost)
  25162.         _.updateLog(player, game, status)
  25163.  
  25164.     
  25165.     def updateLog(_, player, game, status):
  25166.         log = (game.id, game.getGameNumber(format = 0), status, game.gstats.start_time, game.gstats.total_elapsed_time, VERSION_TUPLE, game.getGameScore(), game.getGameScoreCasino(), game.GAME_VERSION)
  25167.         if player is not None and status >= 0:
  25168.             if not _.prev_games.has_key(player):
  25169.                 _.prev_games[player] = []
  25170.             
  25171.             _.prev_games[player].append(log)
  25172.             if not _.all_prev_games.has_key(player):
  25173.                 _.all_prev_games[player] = []
  25174.             
  25175.             _.all_prev_games[player].append(log)
  25176.         
  25177.         if not _.session_games.has_key(player):
  25178.             _.session_games[player] = []
  25179.         
  25180.         _.session_games[player].append(log)
  25181.  
  25182.  
  25183.  
  25184. class Comments:
  25185.     
  25186.     def __init__(_):
  25187.         _.version_tuple = VERSION_TUPLE
  25188.         _.saved = 0
  25189.         _.comments = { }
  25190.  
  25191.     
  25192.     def new(_):
  25193.         return Comments()
  25194.  
  25195.     
  25196.     def setGameComment(_, gameid, text):
  25197.         player = None
  25198.         key = (1, gameid, player)
  25199.         _.comments[key] = str(text)
  25200.  
  25201.     
  25202.     def getGameComment(_, gameid):
  25203.         player = None
  25204.         key = (1, gameid, player)
  25205.         return _.comments.get(key, '')
  25206.  
  25207.  
  25208.  
  25209. class Application:
  25210.     
  25211.     def __init__(_):
  25212.         _.starttimer = Timer('Application.__init__')
  25213.         _.gdb = GAME_DB
  25214.         _.opt = Options()
  25215.         _.startup_opt = _.opt.copy()
  25216.         _.stats = Statistics()
  25217.         _.comments = Comments()
  25218.         _.splashscreen = 1
  25219.         _.debug = 0
  25220.         _.top = None
  25221.         _.top_cursor = None
  25222.         _.menubar = None
  25223.         _.toolbar = None
  25224.         _.canvas = None
  25225.         _.statusbar = None
  25226.         _.game = None
  25227.         _.dataloader = None
  25228.         _.audio = None
  25229.         _.images = None
  25230.         _.subsampled_images = None
  25231.         _.gimages = Struct(border = [], demo = [], logos = [], redeal = [], shade = [], stats = [])
  25232.         _.progress_bg = None
  25233.         _.progress_images = []
  25234.         _.cardset_manager = CardsetManager()
  25235.         _.cardset = None
  25236.         _.tabletile_manager = TileManager()
  25237.         _.tabletile_index = 0
  25238.         _.sample_manager = SampleManager()
  25239.         _.music_manager = MusicManager()
  25240.         _.music_playlist = []
  25241.         _.intro = Struct(progress = None)
  25242.         home = os.path.normpath(gethomedir())
  25243.         config = os.path.normpath(getprefdir(PACKAGE, home))
  25244.         _.dn = Struct(home = home, config = config, plugins = os.path.join(config, 'plugins'), savegames = os.path.join(config, 'savegames'), maint = os.path.join(config, 'maint'))
  25245.         for k, v in _.dn.__dict__.items():
  25246.             v = os.path.normpath(v)
  25247.             _.dn.__dict__[k] = v
  25248.         
  25249.         _.fn = Struct(opt = os.path.join(_.dn.config, 'options.dat'), stats = os.path.join(_.dn.config, 'statistics.dat'), holdgame = os.path.join(_.dn.config, 'holdgame.dat'), comments = os.path.join(_.dn.config, 'comments.dat'))
  25250.         for k, v in _.dn.__dict__.items():
  25251.             v = os.path.normpath(v)
  25252.             _.fn.__dict__[k] = v
  25253.         
  25254.         _.gamerandom = LCRandom64()
  25255.         _.miscrandom = LCRandom64()
  25256.         player = getusername()
  25257.         player = player[:30]
  25258.         _.opt.player = player
  25259.         _.nextgame = Struct(id = 0, random = None, loadedgame = None, startdemo = 0, cardset = None, holdgame = 0, bookmark = None)
  25260.         _.commandline = Struct(loadgame = None)
  25261.         _.demo_counter = 0
  25262.  
  25263.     
  25264.     def mainloop(_):
  25265.         _.startup_opt = _.opt.copy()
  25266.         
  25267.         try:
  25268.             _.loadStatistics()
  25269.         except:
  25270.             pass
  25271.  
  25272.         
  25273.         try:
  25274.             _.loadComments()
  25275.         except:
  25276.             pass
  25277.  
  25278.         if _.getGameClass(_.opt.last_gameid):
  25279.             _.nextgame.id = _.opt.last_gameid
  25280.         
  25281.         id = _.gdb.getGamesIdSortedByName()[0]
  25282.         tmpgame = _.constructGame(id)
  25283.         if _.opt.game_holded > 0 and not (_.nextgame.loadedgame):
  25284.             game = None
  25285.             
  25286.             try:
  25287.                 game = tmpgame._loadGame(_.fn.holdgame, _)
  25288.             except:
  25289.                 game = None
  25290.  
  25291.             if game:
  25292.                 if game.id == _.opt.game_holded and game.gstats.holded:
  25293.                     game.gstats.loaded = game.gstats.loaded - 1
  25294.                     game.gstats.holded = 0
  25295.                     _.nextgame.loadedgame = game
  25296.                 else:
  25297.                     game.destruct()
  25298.                     destruct(game)
  25299.             
  25300.             game = None
  25301.         
  25302.         if _.commandline.loadgame and not (_.nextgame.loadedgame):
  25303.             
  25304.             try:
  25305.                 _.nextgame.loadedgame = tmpgame._loadGame(_.commandline.loadgame, _)
  25306.                 _.nextgame.loadedgame.gstats.holded = 0
  25307.             except:
  25308.                 _.nextgame.loadedgame = None
  25309.  
  25310.         
  25311.         _.opt.game_holded = 0
  25312.         tmpgame.destruct()
  25313.         destruct(tmpgame)
  25314.         tmpgame = None
  25315.         _.menubar = PysolMenubar(_, _.top)
  25316.         _.statusbar = PysolStatusbar(_.top)
  25317.         _.statusbar.show(_.opt.statusbar)
  25318.         dir = _.getToolbarImagesDir(_.opt.toolbar_size)
  25319.         _.toolbar = PysolToolbar(_.top, dir = dir, size = _.opt.toolbar_size)
  25320.         _.toolbar.setRelief(_.opt.toolbar_relief)
  25321.         _.toolbar.show(_.opt.toolbar)
  25322.         if _.intro.progress:
  25323.             _.intro.progress.update(step = 1)
  25324.         
  25325.         _.canvas = MfxCanvas(_.top, bg = _.opt.tablecolor, highlightthickness = 0)
  25326.         _.canvas.pack(fill = 'both', expand = 1)
  25327.         tile = _.tabletile_manager.get(_.tabletile_index)
  25328.         _.canvas.setTile(tile.filename)
  25329.         _.canvas.setTextColor(tile.text_color)
  25330.         
  25331.         try:
  25332.             while 1:
  25333.                 if not __debug__ and _.cardset is not None:
  25334.                     raise AssertionError
  25335.                 (id, random) = (_.nextgame.id, _.nextgame.random)
  25336.                 (_.nextgame.id, _.nextgame.random) = (0, None)
  25337.                 _.runGame(id, random)
  25338.                 if _.nextgame.holdgame:
  25339.                     if not __debug__ and _.nextgame.id <= 0:
  25340.                         raise AssertionError
  25341.                     
  25342.                     try:
  25343.                         _.game.gstats.holded = 1
  25344.                         _.game._saveGame(_.fn.holdgame)
  25345.                         _.opt.game_holded = _.game.id
  25346.                     except:
  25347.                         pass
  25348.  
  25349.                 
  25350.                 _.freeGame()
  25351.                 if _.nextgame.id <= 0:
  25352.                     break
  25353.                 
  25354.                 if _.nextgame.cardset is not _.cardset:
  25355.                     _.loadCardset(_.nextgame.cardset, id = _.nextgame.id, update = 7 + 256)
  25356.                 else:
  25357.                     _.requestCompatibleCardsetType(_.nextgame.id)
  25358.         finally:
  25359.             _.opt.last_gameid = id
  25360.             
  25361.             try:
  25362.                 _.wm_save_state()
  25363.             except:
  25364.                 pass
  25365.  
  25366.             
  25367.             try:
  25368.                 _.saveOptions()
  25369.             except:
  25370.                 pass
  25371.  
  25372.             
  25373.             try:
  25374.                 _.saveStatistics()
  25375.             except:
  25376.                 pass
  25377.  
  25378.             
  25379.             try:
  25380.                 _.saveComments()
  25381.             except:
  25382.                 pass
  25383.  
  25384.             
  25385.             try:
  25386.                 _.audio.destroy()
  25387.             except:
  25388.                 pass
  25389.  
  25390.  
  25391.  
  25392.     
  25393.     def runGame(_, id, random = None):
  25394.         _.top.connectApp(_)
  25395.         g = _.getGameClass(id)
  25396.         if g is None:
  25397.             id = 2
  25398.             random = None
  25399.             g = _.getGameClass(id)
  25400.             if g is None:
  25401.                 id = _.gdb.getGamesIdSortedByName()[0]
  25402.                 g = _.getGameClass(id)
  25403.             
  25404.         
  25405.         gi = _.getGameInfo(id)
  25406.         if __debug__:
  25407.             if not g and type(g) is types.ClassType and id > 0:
  25408.                 raise AssertionError
  25409.         if __debug__:
  25410.             if not gi is not None and gi.id == id:
  25411.                 raise AssertionError
  25412.         _.game = _.constructGame(id)
  25413.         _.gdb.setSelected(id)
  25414.         _.game.busy = 1
  25415.         _.game.create(_)
  25416.         if not (_.nextgame.startdemo) and not (_.nextgame.bookmark):
  25417.             if _.requestCompatibleCardsetSize() > 0:
  25418.                 _.nextgame.id = id
  25419.                 _.nextgame.random = random
  25420.                 return None
  25421.             
  25422.         
  25423.         _.menubar.connectGame(_.game)
  25424.         _.toolbar.connectGame(_.game, _.menubar)
  25425.         _.game.updateStatus(player = _.opt.player)
  25426.         while 1:
  25427.             
  25428.             try:
  25429.                 _.opt.recent_gameid.remove(id)
  25430.             except ValueError:
  25431.                 break
  25432.  
  25433.         _.opt.recent_gameid.insert(0, id)
  25434.         del _.opt.recent_gameid[15:]
  25435.         _.menubar.updateRecentGamesMenu(_.opt.recent_gameid)
  25436.         if _.intro.progress:
  25437.             _.intro.progress.destroy()
  25438.             destruct(_.intro.progress)
  25439.             _.intro.progress = None
  25440.         
  25441.         autoplay = 0
  25442.         if _.nextgame.loadedgame is not None:
  25443.             _.stats.gameid_balance = 0
  25444.             _.game.restoreGame(_.nextgame.loadedgame)
  25445.             destruct(_.nextgame.loadedgame)
  25446.         elif _.nextgame.bookmark is not None:
  25447.             _.game.restoreGameFromBookmark(_.nextgame.bookmark)
  25448.         else:
  25449.             _.stats.gameid_balance = 0
  25450.             _.game.newGame(random = random, autoplay = 0)
  25451.             autoplay = 1
  25452.         _.nextgame.loadedgame = None
  25453.         _.nextgame.bookmark = None
  25454.         if _.debug or bundle & 4:
  25455.             pass
  25456.         elif _.splashscreen > 0:
  25457.             status = helpAbout(_, timeout = 20000, sound = 0)
  25458.             if status == 2:
  25459.                 _.nextgame.startdemo = 1
  25460.             
  25461.         
  25462.         _.splashscreen = 0
  25463.         if _.nextgame.startdemo:
  25464.             _.nextgame.startdemo = 0
  25465.             _.game.startDemo()
  25466.             _.game.createDemoInfoText()
  25467.         elif autoplay:
  25468.             _.game.autoPlay()
  25469.             _.game.stats.player_moves = 0
  25470.         
  25471.         _.game.busy = 0
  25472.         _.top.mainloop()
  25473.  
  25474.     
  25475.     def freeGame(_):
  25476.         _.toolbar.connectGame(None, None)
  25477.         _.menubar.connectGame(None)
  25478.         unbind_destroy(_.canvas)
  25479.         _.canvas.deleteAllItems()
  25480.         _.canvas.update_idletasks()
  25481.         if _.game:
  25482.             _.game.destruct()
  25483.             destruct(_.game)
  25484.         
  25485.         _.game = None
  25486.         _.top.connectApp(None)
  25487.  
  25488.     
  25489.     def dumpMem(_, info = ''):
  25490.         pass
  25491.  
  25492.     
  25493.     def wm_save_state(_):
  25494.         if _.top:
  25495.             s = _.top.wm_state()
  25496.             if s == 'zoomed':
  25497.                 _.opt.wm_maximized = 1
  25498.             elif s == 'normal':
  25499.                 _.opt.wm_maximized = 0
  25500.             
  25501.         
  25502.  
  25503.     
  25504.     def wm_withdraw(_):
  25505.         if _.intro.progress:
  25506.             _.intro.progress.destroy()
  25507.             destruct(_.intro.progress)
  25508.             _.intro.progress = None
  25509.         
  25510.         if _.top:
  25511.             wm_withdraw(_.top)
  25512.             _.top.busyUpdate()
  25513.         
  25514.  
  25515.     
  25516.     def loadImages1(_):
  25517.         dir = os.path.join('images', 'logos')
  25518.         _.gimages.logos.append(loadImage(_.dataloader.findImage('joker07_40_774', dir)))
  25519.         _.gimages.logos.append(loadImage(_.dataloader.findImage('joker08_40_774', dir)))
  25520.         _.gimages.logos.append(_.dataloader.findImage('joker07_50_774', dir))
  25521.         _.gimages.logos.append(_.dataloader.findImage('joker08_50_774', dir))
  25522.         _.gimages.logos.append(_.dataloader.findImage('joker11_100_774', dir))
  25523.         _.gimages.logos.append(_.dataloader.findImage('joker10_100', dir))
  25524.         _.gimages.logos.append(_.dataloader.findImage('pysol_40', dir))
  25525.         dir = 'images'
  25526.         for f in ('stopsign', 'redeal'):
  25527.             _.gimages.redeal.append(_.dataloader.findImage(f, dir))
  25528.         
  25529.  
  25530.     
  25531.     def loadImages2(_):
  25532.         dir = os.path.join('images', 'demo')
  25533.         for f in ('demo01', 'demo02', 'demo03', 'demo04', 'demo05'):
  25534.             _.gimages.demo.append(_.dataloader.findImage(f, dir))
  25535.         
  25536.         dir = os.path.join('images', 'stats')
  25537.         for f in ('barchart',):
  25538.             _.gimages.stats.append(_.dataloader.findImage(f, dir))
  25539.         
  25540.  
  25541.     
  25542.     def loadImages3(_):
  25543.         pass
  25544.  
  25545.     
  25546.     def loadImages4(_):
  25547.         for k, v in _.gimages.__dict__.items():
  25548.             if type(v) is types.ListType:
  25549.                 for i in range(len(v)):
  25550.                     pass
  25551.                 
  25552.                 _.gimages.__dict__[k] = tuple(v)
  25553.             
  25554.         
  25555.  
  25556.     
  25557.     def getToolbarImagesDir(_, size):
  25558.         dir = 'small'
  25559.         if size:
  25560.             dir = 'large'
  25561.         
  25562.         dir = os.path.join(_.dataloader.dir, 'toolbar', dir)
  25563.         return dir
  25564.  
  25565.     
  25566.     def updateCardset(_, id = 0, update = 7):
  25567.         cs = _.images.cs
  25568.         _.cardset = cs
  25569.         _.nextgame.cardset = cs
  25570.         _.cardset_manager.setSelected(cs.index)
  25571.         if update & 1:
  25572.             _.opt.cardset[0] = (cs.name, cs.backname)
  25573.         
  25574.         if update & 2:
  25575.             _.opt.cardset[cs.si.type] = (cs.name, cs.backname)
  25576.         
  25577.         gi = _.getGameInfo(id)
  25578.         if gi:
  25579.             if update & 256:
  25580.                 
  25581.                 try:
  25582.                     del _.opt.cardset[(1, gi.id)]
  25583.                 except KeyError:
  25584.                     pass
  25585.  
  25586.             
  25587.             t = _.checkCompatibleCardsetType(gi, cs)
  25588.             if not t[1]:
  25589.                 if update & 4:
  25590.                     _.opt.cardset[gi.category] = (cs.name, cs.backname)
  25591.                 
  25592.                 if update & 8:
  25593.                     _.opt.cardset[(1, gi.id)] = (cs.name, cs.backname)
  25594.                 
  25595.             
  25596.         
  25597.  
  25598.     
  25599.     def loadCardset(_, cs, id = 0, update = 7, progress = None):
  25600.         r = 0
  25601.         if cs is None or cs.error:
  25602.             return 0
  25603.         
  25604.         if cs is _.cardset:
  25605.             _.updateCardset(id, update = update)
  25606.             return 1
  25607.         
  25608.         if progress is None:
  25609.             _.wm_save_state()
  25610.             _.wm_withdraw()
  25611.             title = 'Loading ' + CARDSET + ' ' + cs.name + '...'
  25612.             color = _.opt.tablecolor
  25613.             if _.tabletile_index > 0:
  25614.                 color = '#008200'
  25615.             
  25616.             progress = PysolProgressBar(_, _.top, title = title, color = color, bg = _.progress_bg, images = _.progress_images)
  25617.         
  25618.         images = Images(_.dataloader, cs)
  25619.         
  25620.         try:
  25621.             if not images.load(app = _, progress = progress):
  25622.                 raise Exception, 'Invalid or damaged ' + CARDSET
  25623.             
  25624.             simages = SubsampledImages(images)
  25625.             if _.images is not None:
  25626.                 _.images.destruct()
  25627.                 destruct(_.images)
  25628.             
  25629.             _.images = images
  25630.             _.subsampled_images = simages
  25631.             _.updateCardset(id, update = update)
  25632.             r = 1
  25633.         except (Exception, TclError, UnpicklingError):
  25634.             ex = None
  25635.             cs.error = 1
  25636.             _.nextgame.cardset = _.cardset
  25637.             if _.cardset:
  25638.                 _.cardset_manager.setSelected(_.cardset.index)
  25639.             
  25640.             images.destruct()
  25641.             destruct(images)
  25642.             d = MfxExceptionDialog(_.top, ex, title = CARDSET + ' load error', text = 'Error while loading ' + CARDSET)
  25643.  
  25644.         _.intro.progress = progress
  25645.         if r and _.menubar is not None:
  25646.             _.menubar.updateBackgroundImagesMenu()
  25647.         
  25648.         return r
  25649.  
  25650.     
  25651.     def checkCompatibleCardsetType(_, gi, cs):
  25652.         if not __debug__ and gi is not None:
  25653.             raise AssertionError
  25654.         if not __debug__ and cs is not None:
  25655.             raise AssertionError
  25656.         gc = gi.category
  25657.         cs_type = cs.si.type
  25658.         (t0, t1) = (None, None)
  25659.         if gc == GI.GC_FRENCH:
  25660.             t0 = 'French'
  25661.             if cs_type not in (CSI.TYPE_FRENCH, CSI.TYPE_TAROCK):
  25662.                 t1 = t0
  25663.             
  25664.         elif gc == GI.GC_HANAFUDA:
  25665.             t0 = 'Hanafuda'
  25666.             if cs_type not in (CSI.TYPE_HANAFUDA,):
  25667.                 t1 = t0
  25668.             
  25669.         elif gc == GI.GC_TAROCK:
  25670.             t0 = 'Tarock'
  25671.             if cs_type not in (CSI.TYPE_TAROCK,):
  25672.                 t1 = t0
  25673.             
  25674.         elif gc == GI.GC_MAHJONGG:
  25675.             t0 = 'Mahjongg'
  25676.             if cs_type not in (CSI.TYPE_MAHJONGG,):
  25677.                 t1 = t0
  25678.             
  25679.         elif gc == GI.GC_HEXADECK:
  25680.             t0 = 'Hex A Deck'
  25681.             if cs_type not in (CSI.TYPE_HEXADECK,):
  25682.                 t1 = t0
  25683.             
  25684.         elif gc == GI.GC_MUGHAL_GANJIFA:
  25685.             t0 = 'Mughal Ganjifa'
  25686.             if cs_type not in (CSI.TYPE_MUGHAL_GANJIFA, CSI.TYPE_NAVAGRAHA_GANJIFA, CSI.TYPE_DASHAVATARA_GANJIFA):
  25687.                 t1 = t0
  25688.             
  25689.         elif gc == GI.GC_NAVAGRAHA_GANJIFA:
  25690.             t0 = 'Navagraha Ganjifa'
  25691.             if cs_type not in (CSI.TYPE_NAVAGRAHA_GANJIFA, CSI.TYPE_DASHAVATARA_GANJIFA):
  25692.                 t1 = t0
  25693.             
  25694.         elif gc == GI.GC_DASHAVATARA_GANJIFA:
  25695.             t0 = 'Dashavatara Ganjifa'
  25696.             if cs_type not in (CSI.TYPE_DASHAVATARA_GANJIFA,):
  25697.                 t1 = t0
  25698.             
  25699.         elif gc == GI.GC_TRUMP_ONLY:
  25700.             t0 = 'Trump only'
  25701.             if cs_type not in (CSI.TYPE_TRUMP_ONLY,):
  25702.                 t1 = t0
  25703.             elif len(cs.trumps) < gi.ncards:
  25704.                 t1 = t0
  25705.             
  25706.         else:
  25707.             t0 = t1 = 'Unknown'
  25708.         return (t0, t1)
  25709.  
  25710.     
  25711.     def getCompatibleCardset(_, gi, cs):
  25712.         if gi is None:
  25713.             return (cs, 1)
  25714.         
  25715.         if cs:
  25716.             t = _.checkCompatibleCardsetType(gi, cs)
  25717.             if not t[1]:
  25718.                 return (cs, 1)
  25719.             
  25720.         
  25721.         for key, flag in (((1, gi.id), 8), (gi.category, 4)):
  25722.             c = _.opt.cardset.get(key)
  25723.             cs = _.cardset_manager.getByName(c[0])
  25724.             if not cs:
  25725.                 continue
  25726.             
  25727.             t = _.checkCompatibleCardsetType(gi, cs)
  25728.             if not t[1]:
  25729.                 cs.updateCardback(backname = c[1])
  25730.                 return (cs, flag)
  25731.             
  25732.         
  25733.         return (None, 0)
  25734.  
  25735.     
  25736.     def requestCompatibleCardsetType(_, id):
  25737.         gi = _.getGameInfo(id)
  25738.         (cs, cs_update_flag) = _.getCompatibleCardset(gi, _.cardset)
  25739.         if cs is _.cardset:
  25740.             return 0
  25741.         
  25742.         _.wm_save_state()
  25743.         _.wm_withdraw()
  25744.         if cs is not None:
  25745.             _.loadCardset(cs, update = 1)
  25746.             return 1
  25747.         
  25748.         t = _.checkCompatibleCardsetType(gi, _.cardset)
  25749.         d = MfxDialog(_.top, title = 'Incompatible ' + CARDSET, bitmap = 'warning', text = 'The currently selected ' + CARDSET + '\n' + _.cardset.name + '\n\nis not compatible with the game\n' + gi.name + '\n\nPlease select a ' + t[0] + ' type ' + CARDSET + '.', strings = ('OK',), default = 0)
  25750.         cs = _._Application__selectCardsetDialog(t)
  25751.         if cs is None:
  25752.             return -1
  25753.         
  25754.         _.loadCardset(cs, id = id)
  25755.         return 1
  25756.  
  25757.     
  25758.     def requestCompatibleCardsetSize(_):
  25759.         (game, gi) = (_.game, _.game.gameinfo)
  25760.         (sw, sh) = (_.top.winfo_screenwidth(), _.top.winfo_screenheight())
  25761.         if sw - game.width >= 0 and sh - game.height >= 32:
  25762.             return 0
  25763.         
  25764.         (cs, cs_update_flag) = _.getCompatibleCardset(gi, None)
  25765.         if cs is _.cardset:
  25766.             if cs_update_flag & 8:
  25767.                 return 0
  25768.             
  25769.             cs = None
  25770.         
  25771.         _.wm_save_state()
  25772.         _.wm_withdraw()
  25773.         if cs is not None:
  25774.             _.loadCardset(cs, update = 1)
  25775.             return 1
  25776.         
  25777.         t = _.checkCompatibleCardsetType(gi, _.cardset)
  25778.         d = MfxDialog(_.top, title = 'Incompatible ' + CARDSET, bitmap = 'warning', text = 'The currently selected ' + CARDSET + '\n' + _.cardset.name + '\n\nis too large for the game\n' + gi.name + '\n\nPlease increase your screen resolution\nor select a smaller ' + CARDSET + '.', strings = ('Select ' + CARDSET + '...', 'Continue'), default = 0)
  25779.         if d.status != 0:
  25780.             return -1
  25781.         
  25782.         if d.button == 1:
  25783.             _.updateCardset(_.game.id, update = 8)
  25784.             return 0
  25785.         
  25786.         cs = _._Application__selectCardsetDialog(t)
  25787.         if cs is None:
  25788.             return -1
  25789.         
  25790.         _.loadCardset(cs, id = _.game.id, update = 9)
  25791.         return 1
  25792.  
  25793.     
  25794.     def __selectCardsetDialog(_, t):
  25795.         key = _.cardset.index
  25796.         d = SelectCardsetByTypeDialogWithPreview(_.top, title = 'Please select a ' + t[0] + ' type ' + CARDSET, app = _, manager = _.cardset_manager, key = key, strings = (None, 'OK', 'Cancel'), default = 1)
  25797.         if d.status != 0 or d.button != 1:
  25798.             return None
  25799.         
  25800.         cs = _.cardset_manager.get(d.key)
  25801.         if cs is None or d.key == key:
  25802.             return None
  25803.         
  25804.         return cs
  25805.  
  25806.     
  25807.     def loadOptions(_):
  25808.         _.opt.setDefaults(_.top)
  25809.         opt = unpickle(_.fn.opt)
  25810.         if opt:
  25811.             cardset = _.opt.cardset
  25812.             if hasattr(opt, 'version_tuple') and hasattr(opt, 'cardset'):
  25813.                 merge_dict(cardset, opt.cardset)
  25814.             
  25815.             merge_dict(_.opt.__dict__, opt.__dict__)
  25816.             _.opt.cardset = cardset
  25817.         
  25818.         _.opt.setConstants()
  25819.  
  25820.     
  25821.     def loadStatistics(_):
  25822.         stats = unpickle(_.fn.stats)
  25823.         if stats:
  25824.             merge_dict(_.stats.__dict__, stats.__dict__)
  25825.         
  25826.         _.stats.session_games = { }
  25827.         _.stats.session_balance = { }
  25828.         _.stats.gameid_balance = 0
  25829.  
  25830.     
  25831.     def loadComments(_):
  25832.         comments = unpickle(_.fn.comments)
  25833.         if comments:
  25834.             merge_dict(_.comments.__dict__, comments.__dict__)
  25835.         
  25836.  
  25837.     
  25838.     def __saveObject(_, obj, fn):
  25839.         obj.version_tuple = VERSION_TUPLE
  25840.         obj.saved = obj.saved + 1
  25841.         pickle(obj, fn, binmode = 1)
  25842.  
  25843.     
  25844.     def saveOptions(_):
  25845.         _._Application__saveObject(_.opt, _.fn.opt)
  25846.  
  25847.     
  25848.     def saveStatistics(_):
  25849.         _._Application__saveObject(_.stats, _.fn.stats)
  25850.  
  25851.     
  25852.     def saveComments(_):
  25853.         _._Application__saveObject(_.comments, _.fn.comments)
  25854.  
  25855.     
  25856.     def constructGame(_, id):
  25857.         gi = _.gdb.get(id)
  25858.         if gi is None:
  25859.             raise Exception, 'Unknown game (id %d)' % id
  25860.         
  25861.         return gi.gameclass(gi)
  25862.  
  25863.     
  25864.     def getGamesIdSortedById(_):
  25865.         return _.gdb.getGamesIdSortedById()
  25866.  
  25867.     
  25868.     def getGamesIdSortedByName(_):
  25869.         return _.gdb.getGamesIdSortedByName()
  25870.  
  25871.     
  25872.     def getGameInfo(_, id):
  25873.         return _.gdb.get(id)
  25874.  
  25875.     
  25876.     def getGameClass(_, id):
  25877.         gi = _.gdb.get(id)
  25878.         if gi is None:
  25879.             return None
  25880.         
  25881.         return gi.gameclass
  25882.  
  25883.     
  25884.     def getGameTitleName(_, id):
  25885.         gi = _.gdb.get(id)
  25886.         if gi is None:
  25887.             return None
  25888.         
  25889.         return gi.name
  25890.  
  25891.     
  25892.     def getGameMenuitemName(_, id):
  25893.         gi = _.gdb.get(id)
  25894.         if gi is None:
  25895.             return None
  25896.         
  25897.         return gi.short_name
  25898.  
  25899.     
  25900.     def getGameRulesFilename(_, id):
  25901.         gi = _.gdb.get(id)
  25902.         if gi is None:
  25903.             return None
  25904.         
  25905.         if gi.rules_filename is not None:
  25906.             return gi.rules_filename
  25907.         
  25908.         n = gi.name
  25909.         n = re.sub('[\\[\\(].*$', '', n)
  25910.         n = latin1_to_ascii(n)
  25911.         n = re.sub('[^\\w]', '', n)
  25912.         n = string.lower(n) + '.html'
  25913.         gi.rules_filename = n
  25914.         return n
  25915.  
  25916.     
  25917.     def getGameSaveName(_, id):
  25918.         n = _.getGameTitleName(id)
  25919.         if not n:
  25920.             return None
  25921.         
  25922.         m = re.search('^(.*)([\\[\\(](\\w+).*[\\]\\)])\\s*$', n)
  25923.         if m:
  25924.             n = m.group(1) + '_' + string.lower(m.group(2))
  25925.         
  25926.         n = latin1_to_ascii(n)
  25927.         return re.sub('[^\\w\\-]', '', n)
  25928.  
  25929.     
  25930.     def getRandomGameId(_):
  25931.         return _.miscrandom.choice(_.gdb.getGamesIdSortedById())
  25932.  
  25933.     
  25934.     def loadPlugins(_, dir):
  25935.         if not dir and not os.path.isdir(dir) or bundle & 4:
  25936.             return None
  25937.         
  25938.         names = os.listdir(dir)
  25939.         names = map(os.path.normcase, names)
  25940.         names.sort()
  25941.         for name in names:
  25942.             m = re.search('^(.+)\\.py$', name)
  25943.             n = os.path.join(dir, name)
  25944.         
  25945.  
  25946.     
  25947.     def _readCardsetConfig(_, dir, filename):
  25948.         f = None
  25949.         
  25950.         try:
  25951.             f = open(filename, 'r')
  25952.             lines = f.readlines()
  25953.         finally:
  25954.             if f:
  25955.                 f.close()
  25956.             
  25957.  
  25958.         lines = map(string.strip, lines)
  25959.         if string.index(lines[0], 'PySol') != 0:
  25960.             return None
  25961.         
  25962.         config = CardsetConfig()
  25963.         if not _._parseCardsetConfig(config, lines):
  25964.             return None
  25965.         
  25966.         if config.CARDD > _.top.winfo_screendepth():
  25967.             return None
  25968.         
  25969.         cs = Cardset()
  25970.         cs.dir = dir
  25971.         cs.update(config.__dict__)
  25972.         return cs
  25973.  
  25974.     
  25975.     def _parseCardsetConfig(_, cs, line):
  25976.         if len(line) < 6:
  25977.             return 0
  25978.         
  25979.         fields = filter(None, re.split(';', line[0]))
  25980.         fields = map(string.strip, fields)
  25981.         if len(fields) >= 2:
  25982.             m = re.search('^(\\d+)$', fields[1])
  25983.             if m:
  25984.                 cs.version = int(m.group(1))
  25985.             
  25986.         
  25987.         if cs.version >= 3:
  25988.             if len(fields) < 5:
  25989.                 return 0
  25990.             
  25991.             cs.ext = fields[2]
  25992.             m = re.search('^(\\d+)$', fields[3])
  25993.             if not m:
  25994.                 return 0
  25995.             
  25996.             cs.type = int(m.group(1))
  25997.             m = re.search('^(\\d+)$', fields[4])
  25998.             if not m:
  25999.                 return 0
  26000.             
  26001.             cs.ncards = int(m.group(1))
  26002.         
  26003.         if cs.version >= 4:
  26004.             if len(fields) < 6:
  26005.                 return 0
  26006.             
  26007.             styles = string.splitfields(fields[5], ',')
  26008.             for s in styles:
  26009.                 m = re.search('^\\s*(\\d+)\\s*$', s)
  26010.                 s = int(m.group(1))
  26011.                 if not (s in cs.styles):
  26012.                     cs.styles.append(s)
  26013.                 
  26014.             
  26015.         
  26016.         if cs.version >= 5:
  26017.             if len(fields) < 7:
  26018.                 return 0
  26019.             
  26020.             m = re.search('^(\\d+)$', fields[6])
  26021.             if not m:
  26022.                 return 0
  26023.             
  26024.             cs.year = int(m.group(1))
  26025.         
  26026.         if len(cs.ext) < 2 or cs.ext[0] != '.':
  26027.             return 0
  26028.         
  26029.         if not line[1]:
  26030.             return 0
  26031.         
  26032.         cs.ident = line[1]
  26033.         m = re.search('^(.*;)?([^;]+)$', cs.ident)
  26034.         if not m:
  26035.             return 0
  26036.         
  26037.         cs.name = string.strip(m.group(2))
  26038.         m = re.search('^(\\d+)\\s+(\\d+)\\s+(\\d+)', line[2])
  26039.         if not m:
  26040.             return 0
  26041.         
  26042.         (cs.CARDW, cs.CARDH, cs.CARDD) = (int(m.group(1)), int(m.group(2)), int(m.group(3)))
  26043.         m = re.search('^(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)', line[3])
  26044.         if not m:
  26045.             return 0
  26046.         
  26047.         cs.CARD_UP_YOFFSET = int(m.group(1))
  26048.         cs.SHADOW_XOFFSET = int(m.group(3))
  26049.         cs.SHADOW_YOFFSET = int(m.group(4))
  26050.         back = line[4]
  26051.         if not back:
  26052.             return 0
  26053.         
  26054.         cs.backnames = re.split(';', line[5])
  26055.         cs.backnames = map(string.strip, cs.backnames)
  26056.         cs.backnames = filter(None, cs.backnames)
  26057.         if back in cs.backnames:
  26058.             cs.backindex = cs.backnames.index(back)
  26059.         else:
  26060.             cs.backnames.insert(0, back)
  26061.             cs.backindex = 0
  26062.         return 1
  26063.  
  26064.     
  26065.     def initCardsets(_):
  26066.         manager = _.cardset_manager
  26067.         dirs = manager.getSearchDirs(_, ('cardsets', ''), 'PYSOL_CARDSETS')
  26068.         if _.debug:
  26069.             dirs = dirs + manager.getSearchDirs(_, 'cardsets-*')
  26070.         
  26071.         
  26072.         try:
  26073.             dirs = dirs + manager.getRegistryDirs(_, ('PySol_Cardsets', 'Cardsets'))
  26074.         except:
  26075.             pass
  26076.  
  26077.         (found, t) = ([], { })
  26078.         for dir in dirs:
  26079.             dir = string.strip(dir)
  26080.             
  26081.             try:
  26082.                 names = []
  26083.                 for name in names:
  26084.                     m = re.search('^cardset-', name, re.I)
  26085.                     d = os.path.join(dir, name)
  26086.                     if not os.path.isdir(d):
  26087.                         continue
  26088.                     
  26089.                     f1 = os.path.join(d, 'config.txt')
  26090.                     f2 = os.path.join(d, 'COPYRIGHT')
  26091.                     if os.path.isfile(f1) and os.path.isfile(f2):
  26092.                         
  26093.                         try:
  26094.                             cs = _._readCardsetConfig(d, f1)
  26095.                             if cs:
  26096.                                 back = cs.backnames[cs.backindex]
  26097.                                 f1 = os.path.join(d, back)
  26098.                                 f2 = os.path.join(d, 'shade' + cs.ext)
  26099.                                 if os.path.isfile(f1) and os.path.isfile(f2):
  26100.                                     found.append(cs)
  26101.                                 
  26102.                         except:
  26103.                             pass
  26104.  
  26105.                     
  26106.             except EnvError:
  26107.                 0
  26108.                 ex = 0
  26109.                 dirs
  26110.             except:
  26111.                 0
  26112.  
  26113.         
  26114.         for obj in found:
  26115.             pass
  26116.         
  26117.  
  26118.     
  26119.     def initTiles(_):
  26120.         manager = _.tabletile_manager
  26121.         dirs = manager.getSearchDirs(_, 'tiles-*', 'PYSOL_TILES')
  26122.         
  26123.         try:
  26124.             dirs = dirs + manager.getRegistryDirs(_, 'Tiles')
  26125.         except:
  26126.             pass
  26127.  
  26128.         s = '((\\' + string.join(IMAGE_EXTENSIONS, ')|(\\') + '))$'
  26129.         ext_re = re.compile(s, re.I)
  26130.         text_color_re = re.compile('^(.+)-([0-9A-Fa-f]{6})$')
  26131.         (found, t) = ([], { })
  26132.         for dir in dirs:
  26133.             dir = string.strip(dir)
  26134.             
  26135.             try:
  26136.                 names = []
  26137.                 for name in names:
  26138.                     f = os.path.join(dir, name)
  26139.                     if not os.path.isfile(f):
  26140.                         continue
  26141.                     
  26142.                     tile = Tile()
  26143.                     tile.filename = f
  26144.                     n = ext_re.sub('', string.strip(name))
  26145.                     m = text_color_re.search(n)
  26146.                     if m:
  26147.                         n = m.group(1)
  26148.                         tile.text_color = '#' + string.lower(m.group(2))
  26149.                     
  26150.                     n = re.sub('_', ' ', n)
  26151.                     tile.name = n
  26152.                     key = string.lower(n)
  26153.                     if not t.has_key(key):
  26154.                         t[key] = 1
  26155.                         found.append((n, tile))
  26156.                     
  26157.             except EnvError:
  26158.                 0
  26159.                 ex = 0
  26160.                 dirs
  26161.             except:
  26162.                 0
  26163.  
  26164.         
  26165.         found.sort()
  26166.         for f in found:
  26167.             obj = f[1]
  26168.         
  26169.  
  26170.     
  26171.     def initResource(_, manager, dirs, ext_re, Resource_Class):
  26172.         (found, t) = ([], { })
  26173.         for dir in dirs:
  26174.             dir = string.strip(dir)
  26175.             
  26176.             try:
  26177.                 names = []
  26178.                 if dir and os.path.isdir(dir):
  26179.                     names = os.listdir(dir)
  26180.                     names = map(os.path.normcase, names)
  26181.                     names.sort()
  26182.                 
  26183.                 for name in names:
  26184.                     f = os.path.join(dir, name)
  26185.                     f = os.path.normpath(f)
  26186.                     if not os.path.isfile(f):
  26187.                         continue
  26188.                     
  26189.                     obj = Resource_Class()
  26190.                     obj.filename = f
  26191.                     n = ext_re.sub('', string.strip(name))
  26192.                     obj.name = n
  26193.                     key = string.lower(n)
  26194.                     if not t.has_key(key):
  26195.                         t[key] = 1
  26196.                         found.append((n, obj))
  26197.                     
  26198.             except EnvError:
  26199.                 None if dir else dirs
  26200.                 ex = None if dir else dirs
  26201.             except:
  26202.                 None if dir else dirs
  26203.  
  26204.         
  26205.         found.sort()
  26206.         if manager:
  26207.             for f in found:
  26208.                 obj = f[1]
  26209.             
  26210.         
  26211.         return found
  26212.  
  26213.     
  26214.     def initSamples(_):
  26215.         manager = _.sample_manager
  26216.         dirs = manager.getSearchDirs(_, ('sound', os.path.join('sound', 'extra')))
  26217.         ext_re = re.compile('\\.((wav))$', re.I)
  26218.         _.initResource(manager, dirs, ext_re, Sample)
  26219.  
  26220.     
  26221.     def initMusic(_):
  26222.         manager = _.music_manager
  26223.         dirs = manager.getSearchDirs(_, 'music-*', 'PYSOL_MUSIC')
  26224.         
  26225.         try:
  26226.             dirs = dirs + manager.getRegistryDirs(_, 'Music')
  26227.         except:
  26228.             pass
  26229.  
  26230.         ext_re = re.compile('\\.((it)|(mod)|(mp3)|(pym)|(s3m)|(xm))$', re.I)
  26231.         _.initResource(manager, dirs, ext_re, Music)
  26232.  
  26233.  
  26234.  
  26235. def fatal_no_cardsets(app):
  26236.     app.wm_withdraw()
  26237.     d = MfxDialog(app.top, title = PACKAGE + ' installation error', text = 'No ' + CARDSET + 's were found !!!\n\nMain data directory is:\n' + app.dataloader.dir + '\n\nPlease check your ' + PACKAGE + ' installation.', bitmap = 'error', strings = ('Quit',))
  26238.  
  26239.  
  26240. def pysol_init(app, args):
  26241.     
  26242.     try:
  26243.         os.makedirs(app.dn.config, 488)
  26244.     except:
  26245.         pass
  26246.  
  26247.     
  26248.     try:
  26249.         os.mkdir(os.path.join(app.dn.savegames), 488)
  26250.     except:
  26251.         pass
  26252.  
  26253.     
  26254.     try:
  26255.         os.mkdir(os.path.join(app.dn.config, 'music'), 488)
  26256.     except:
  26257.         pass
  26258.  
  26259.     
  26260.     try:
  26261.         os.mkdir(os.path.join(app.dn.config, 'screenshots'), 488)
  26262.     except:
  26263.         pass
  26264.  
  26265.     
  26266.     try:
  26267.         os.mkdir(os.path.join(app.dn.config, 'tiles'), 488)
  26268.     except:
  26269.         pass
  26270.  
  26271.     f = os.path.join('html', 'license.html')
  26272.     app.dataloader = DataLoader(args[0], f)
  26273.     wm_command = ''
  26274.     opt_nosound = 0
  26275.     prog = sys.executable
  26276.     argv0 = os.path.normpath(args[0])
  26277.     if prog and os.path.isfile(prog):
  26278.         prog = os.path.abspath(prog)
  26279.         if os.path.isfile(argv0):
  26280.             wm_command = prog + ' ' + os.path.abspath(argv0)
  26281.         
  26282.     
  26283.     for a in args[1:]:
  26284.         if os.path.isfile(a):
  26285.             app.commandline.loadgame = a
  26286.         elif a == '--nodebug':
  26287.             app.debug = 0
  26288.         elif a == '--debug':
  26289.             app.debug = app.debug + 1
  26290.         elif a[:9] == '--bindir=':
  26291.             wm_command = wm_command + ' ' + a
  26292.         elif a == '--bundle':
  26293.             bundle = bundle + 1
  26294.         elif a == '--nosound':
  26295.             opt_nosound = 1
  26296.             wm_command = wm_command + ' ' + a
  26297.         
  26298.     
  26299.     top = MfxRoot(className = PACKAGE)
  26300.     app.top = top
  26301.     app.top_cursor = top.cget('cursor')
  26302.     
  26303.     try:
  26304.         app.loadOptions()
  26305.     except EnvError:
  26306.         ex = None
  26307.     except:
  26308.         pass
  26309.  
  26310.     if not (app.debug):
  26311.         app.dn.maint = None
  26312.     
  26313.     if 1 and app.debug >= 2:
  26314.         print 'prog:', prog
  26315.         print 'argv0:', argv0
  26316.         print 'wm_command:', wm_command
  26317.         print 'sys.executable:', sys.executable
  26318.         print 'sys.version:', sys.version
  26319.         print 'sys.platform:', sys.platform
  26320.         print 'sys.argv:', sys.argv
  26321.         print 'sys.path:', sys.path
  26322.         print 'dataloader:', app.dataloader.__dict__
  26323.         print 'tkname:', tkname
  26324.         print 'tkversion:', tkversion
  26325.     
  26326.     if 1 and app.debug >= 2:
  26327.         GI().assertGI(app.gdb)
  26328.     
  26329.     app.audio = None
  26330.     if not opt_nosound:
  26331.         if os.name == 'nt' and app.opt.sound_mode == 0:
  26332.             app.audio = Win32AudioClient()
  26333.         elif pysolsoundserver:
  26334.             app.audio = PysolSoundServerModuleClient()
  26335.         elif os.name == 'nt':
  26336.             app.audio = Win32AudioClient()
  26337.         
  26338.     
  26339.     if app.audio:
  26340.         app.audio.startServer()
  26341.         if app.audio.server is None:
  26342.             if os.name == 'nt' and not isinstance(app.audio, Win32AudioClient):
  26343.                 app.audio.destroy()
  26344.                 app.audio = Win32AudioClient()
  26345.                 app.audio.startServer()
  26346.             
  26347.         
  26348.     else:
  26349.         app.audio = AbstractAudioClient()
  26350.     if isinstance(app.audio, PysolSoundServerModuleClient):
  26351.         app.opt.sound_mode = 1
  26352.     else:
  26353.         app.opt.sound_mode = 0
  26354.     top.wm_group(top)
  26355.     top.wm_title(PACKAGE + ' ' + VERSION)
  26356.     top.wm_iconname(PACKAGE + ' ' + VERSION)
  26357.     top.wm_minsize(200, 200)
  26358.     top.wm_protocol('WM_DELETE_WINDOW', top.wmDeleteWindow)
  26359.     if wm_command:
  26360.         top.wm_command(wm_command)
  26361.     
  26362.     (sw, sh, sd) = (top.winfo_screenwidth(), top.winfo_screenheight(), top.winfo_screendepth())
  26363.     if sw < 640 and sh < 480 or sd < 8:
  26364.         app.wm_withdraw()
  26365.         d = MfxDialog(top, title = PACKAGE + ' init error', text = PACKAGE + ' requires a minimum screen resolution\nof 640x480 in 256 colors.', bitmap = 'error', strings = ('Quit',))
  26366.         return 1
  26367.     
  26368.     if 1:
  26369.         top.config(width = min(800, sw - 64), height = min(600, sh - 64), bg = app.opt.tablecolor)
  26370.     else:
  26371.         top.config(bg = app.opt.tablecolor)
  26372.     
  26373.     try:
  26374.         wm_set_icon(top, app.dataloader.findIcon())
  26375.     except:
  26376.         pass
  26377.  
  26378.     if os.name == 'posix':
  26379.         color = '#d9d9d9'
  26380.         top.tk_setPalette('background', color, 'activeBackground', color)
  26381.     
  26382.     if len(app.gdb.getGamesIdSortedByName()) == 0:
  26383.         app.wm_withdraw()
  26384.         d = MfxDialog(top, title = PACKAGE + ' installation error', text = 'No games were found !!!\n\nMain data directory is:\n' + app.dataloader.dir + '\n\nPlease check your ' + PACKAGE + ' installation.', bitmap = 'error', strings = ('Quit',))
  26385.         return 1
  26386.     
  26387.     app.initCardsets()
  26388.     cardset = None
  26389.     c = app.opt.cardset.get(0)
  26390.     if c:
  26391.         cardset = app.cardset_manager.getByName(c[0])
  26392.         if cardset and c[1]:
  26393.             cardset.updateCardback(backname = c[1])
  26394.         
  26395.     
  26396.     if not cardset:
  26397.         cardset = app.cardset_manager.get(0)
  26398.     
  26399.     if app.cardset_manager.len() == 0 or not cardset:
  26400.         fatal_no_cardsets(app)
  26401.         return 3
  26402.     
  26403.     manager = app.tabletile_manager
  26404.     tile = Tile()
  26405.     tile.name = 'None'
  26406.     tile.filename = None
  26407.     manager.register(tile)
  26408.     app.initTiles()
  26409.     if app.opt.tabletile_name:
  26410.         for tile in manager.getAll():
  26411.             pass
  26412.         
  26413.     
  26414.     manager.setSelected(app.tabletile_index)
  26415.     app.initSamples()
  26416.     app.initMusic()
  26417.     app.audio.connectServer(app)
  26418.     if app.audio.audiodev is None:
  26419.         app.opt.sound = 0
  26420.     
  26421.     app.audio.updateSettings()
  26422.     if app.audio.audiodev:
  26423.         music = app.music_manager.getAll()
  26424.         if music:
  26425.             app.music_playlist = list(music)[:]
  26426.             app.miscrandom.shuffle(app.music_playlist)
  26427.             if 1:
  26428.                 for m in app.music_playlist:
  26429.                     pass
  26430.                 
  26431.             
  26432.             app.audio.playContinuousMusic(app.music_playlist)
  26433.         
  26434.     
  26435.     app.progress_bg = '#c0c0c0'
  26436.     app.loadImages1()
  26437.     if not (app.progress_images):
  26438.         app.progress_images = (app.gimages.logos[0], app.gimages.logos[1])
  26439.     
  26440.     app.wm_withdraw()
  26441.     warn_thread = 0
  26442.     warn_pysolsoundserver = 0
  26443.     if not opt_nosound and os.name == 'posix' and pysolsoundserver is None:
  26444.         if 1 and app.opt.sound and re.search('linux', sys.platform, re.I):
  26445.             warn_pysolsoundserver = 1
  26446.             if thread is None:
  26447.                 warn_thread = 1
  26448.             
  26449.         
  26450.         if thread is None:
  26451.             print PACKAGE + ': Python thread module not found, sound disabled.'
  26452.         else:
  26453.             print PACKAGE + ': pysolsoundserver module not found, sound disabled.'
  26454.         sys.stdout.flush()
  26455.     
  26456.     if warn_thread:
  26457.         top.update()
  26458.         d = MfxDialog(top, title = PACKAGE + ' installation problem', text = 'Your Python installation is compiled without thread support.\n\nSounds and background music will be disabled.', bitmap = 'warning', strings = ('OK',))
  26459.     elif warn_pysolsoundserver:
  26460.         top.update()
  26461.         d = MfxDialog(top, title = PACKAGE + ' installation problem', text = 'The pysolsoundserver module was not found.\n\nSounds and background music will be disabled.', bitmap = 'warning', strings = ('OK',))
  26462.     
  26463.     title = 'Welcome to ' + PACKAGE
  26464.     color = app.opt.tablecolor
  26465.     if app.tabletile_index > 0:
  26466.         color = '#008200'
  26467.     
  26468.     app.intro.progress = PysolProgressBar(app, top, title = title, color = color, bg = app.progress_bg, images = app.progress_images)
  26469.     app.loadImages2()
  26470.     app.loadImages3()
  26471.     app.loadImages4()
  26472.     progress = app.intro.progress
  26473.     if not app.loadCardset(cardset, progress = progress, update = 1):
  26474.         for cardset in app.cardset_manager.getAll():
  26475.             progress.reset()
  26476.         else:
  26477.             return 3
  26478.     
  26479.     return 0
  26480.  
  26481.  
  26482. def pysol_exit(app):
  26483.     if app.audio is not None:
  26484.         app.audio.destroy()
  26485.         destruct(app.audio)
  26486.     
  26487.     app.wm_withdraw()
  26488.     if app.canvas is not None:
  26489.         app.canvas.destroy()
  26490.         destruct(app.canvas)
  26491.     
  26492.     if app.toolbar is not None:
  26493.         app.toolbar.destroy()
  26494.         destruct(app.toolbar)
  26495.     
  26496.     if app.menubar is not None:
  26497.         destruct(app.menubar)
  26498.     
  26499.     top = app.top
  26500.     destruct(app)
  26501.     app = None
  26502.     if top is not None:
  26503.         
  26504.         try:
  26505.             top.destroy()
  26506.         except:
  26507.             pass
  26508.  
  26509.         destruct(top)
  26510.     
  26511.  
  26512.  
  26513. def pysol_main(args):
  26514.     app = Application()
  26515.     
  26516.     try:
  26517.         r = pysol_init(app, args)
  26518.         if r != 0:
  26519.             return r
  26520.         
  26521.         app.mainloop()
  26522.     except KeyboardInterrupt:
  26523.         ex = None
  26524.         print 'Exiting on SIGINT.'
  26525.     except StandardError:
  26526.         ex = None
  26527.         if not (app.top):
  26528.             raise 
  26529.         
  26530.         t = str(ex.__class__)
  26531.         if str(ex):
  26532.             t = t + ':\n' + str(ex)
  26533.         
  26534.         d = MfxDialog(app.top, title = PACKAGE + ' internal error', text = 'Internal errror. Please report this bug:\n\n' + t, strings = ('Quit',), bitmap = 'error')
  26535.  
  26536.     
  26537.     try:
  26538.         pysol_exit(app)
  26539.     except:
  26540.         pass
  26541.  
  26542.     return 0
  26543.  
  26544.  
  26545. def main(args = None):
  26546.     if not hasattr(sys, 'platform'):
  26547.         sys.platform = 'unknown'
  26548.     
  26549.     if not hasattr(sys, 'executable'):
  26550.         sys.executable = None
  26551.     
  26552.     if not hasattr(os, 'defpath'):
  26553.         os.defpath = ''
  26554.     
  26555.     if sys.platform[:4] != 'java':
  26556.         if sys.version[:5] < '1.5.2':
  26557.             print '%s needs Python 1.5.2 or better (you have %s)' % (PACKAGE, sys.version)
  26558.             return 1
  26559.         
  26560.     
  26561.     if not __debug__ and len(tkversion) == 4:
  26562.         raise AssertionError
  26563.     if tkname == 'tk':
  26564.         if tkversion < (8, 0, 0, 0):
  26565.             print '%s needs Tcl/Tk 8.0 or better (you have %s)' % (PACKAGE, str(tkversion))
  26566.             return 1
  26567.         
  26568.         if not hasattr(Tkinter.Wm, 'wm_aspect') or not hasattr(Tkinter.Canvas, 'tag_lower'):
  26569.             print '%s: please update the Python-Tk bindings (aka Tkinter) to version 1.5.2 or better' % (PACKAGE,)
  26570.             return 1
  26571.         
  26572.     
  26573.     if -1 % 13 != 12:
  26574.         raise Exception, '-1 % 13 != 12'
  26575.     
  26576.     r = pysol_main(args)
  26577.     return r
  26578.  
  26579. sys.exit(main(sys.argv))
  26580.